Trendence is a Berlin-based HR data and analytics company. We believe data visualization should be modern, AI-ready, and accessible. That’s why we’ve been building TREVL — a custom DSL (domain-specific language) designed to make chart creation as simple as writing a few lines of YAML. A language humans easily can use and robots love.
💡 The idea
You describe a visualization in YAML. The engine fetches data, transforms it, and produces a Highcharts chart. You never write JavaScript for the chart itself — you declare what you want, and TREVL figures out the rest.
A TREVL component answers three questions:
- What data? — Queries (Cube) or api-parameters (TIE API) define what to fetch
- How to transform? — Computed fields and postprocess shape the raw data
- How to display? — Highcharts configuration controls the visual output
⚙️ Rendering pipeline
There are two rendering paths. Both take the same YAML input and produce the same Highcharts JSON output.
x-middle (production)
The original renderer. A TypeScript service that sits between the Trendence App and CubeJS.
Trendence App
-> sends TREVL YAML to x-middle API
-> x-middle resolves template (merges shared config with component)
-> x-middle queries CubeJS (one query per context)
-> x-middle runs computed fields (JavaScript, per row)
-> x-middle runs postprocess (JavaScript, full dataset)
-> x-middle assembles Highcharts JSON
-> App frontend renders chart with Highcharts library
Trevl::Renderer (v3, Ruby)
The new in-process renderer. Runs inside the Rails app — no external service needed. 🚀
Rails app
-> Trevl::Renderer.render(component)
-> resolves template
-> DataSource.for(api).fetch(endpoint, params)
-> runs computed fields via ExecJS
-> runs postprocess via ExecJS
-> builds Highcharts JSON
-> returns {type, highchartsData, warnings}
The Ruby renderer supports pluggable data sources through a registry:
- Cube — standard CubeJS queries (same as x-middle)
- TIE — Trendence Intelligence Engine REST API
- Custom sources can be registered with
Trevl::DataSource.register("name", MySource)
We started out using Cube.js as a semantic data layer to express data queries. Cube remains the primary data source for our HRM product, but the architecture is designed to be pluggable — the goal is to be able to connect any suitable data API as a TREVL data source.
Note: Cube and TIE are currently proprietary integrations within the Trendence platform. We’re working towards opening the data source interface so you can plug in your own APIs.
🔗 How variables work
TREVL uses $-prefixed references to bind data at render time. Think of them as placeholders that get replaced with real values when the chart is built.
Cube references
$Cube.field — A value from the query result (dimension or measure)
y: "$hrmOptionDistributionAcademics.weightedShare"
TIE API references
$endpoint.data.field — A value from a TIE API row
y: "$salary.data.q50"
$endpoint.meta.field — A value from TIE API response metadata
total: "$vacancies-count.meta.total_results"
$resource.endpoint.data.field — Resource-qualified reference for non-default resources
y: "$surveys.option-distribution.data.share"
See TIE API for the full reference syntax.
Computed fields and parameters
$computedFieldName — The result of a computed field you defined
name: "$name" # where 'name' was defined as: code: '"Zielgruppe"'
color: "$color" # where 'color' was defined as: code: '"#003F85"'
$param_name — A runtime parameter from the user’s filter selection
parameter: "$param_geschlecht_zielgruppe"
Resolution priority:
- Computed field results (highest)
- Query result / TIE API data fields
- Parameters (lowest)
🗂️ Cube models
TREVL queries reference CubeJS models. In HRM, each target group has its own Cube model with identical structure:
| Cube Model | Target Group |
|---|---|
hrmOptionDistributionAcademics |
Akademiker_innen |
hrmOptionDistributionSkilledworkers |
Fachkräfte |
hrmOptionDistributionGraduates |
Studierende |
hrmOptionDistributionPupils |
Schüler_innen |
This means a chart for Fachkräfte looks exactly like one for Akademiker_innen — only the Cube model name changes. Templates leverage this by parameterizing the model through the suffix convention (see Templates).