Reference Entry
From Raw API Payloads to Browser Dashboards
Guides · querying · order 30
A practical pattern for turning raw API responses into local-first dashboard interactions with Querylight TS and its JSON DSL.
Relevant APIs
From Raw API Payloads to Browser Dashboards
The dashboard demo shows how Querylight TS can drive browser-side filtering, slicing, and aggregation over raw JSON records.
If an API gives you rows of JSON instead of pre-aggregated charts, you can still build a usable analytics surface in the browser:
- download or collect raw records
- normalize them into a stable local shape
- index the fields you want to filter and aggregate
- derive subsets with queries
- turn those subsets into chart series
That is the pattern used in the demo dashboard.
Why this pattern is useful
Many APIs return data in one of these shapes:
- events
- measurements
- time-series rows
- records with tags, categories, and timestamps
That is enough for exploration, but not enough for a ready-made dashboard. Normally you would solve that with:
- a backend analytics service
- a warehouse
- SQL transformation jobs
- custom aggregation endpoints
For datasets that still fit comfortably in browser memory, prototypes, internal tools, and static demos, that can be overkill.
A good document shape for analytics
The core idea is to treat each API row as a document and index the parts you want to slice on.
const index = new DocumentIndex({
country: new TextFieldIndex(tagAnalyzer, tagAnalyzer),
indicatorId: new TextFieldIndex(tagAnalyzer, tagAnalyzer),
year: new NumericFieldIndex(),
value: new NumericFieldIndex()
});
index.index({
id: "usa-pop-2024",
fields: {
country: ["United States"],
indicatorId: ["SP.POP.TOTL"],
year: ["2024"],
value: ["340110988"]
}
});
That is not very different from indexing documents for search. The difference is what you do with the matching ids afterward.
Use JSON DSL queries to define a subset
In the dashboard demo, charts start with a filtered subset:
const response = await searchJsonDsl({
index,
request: {
query: {
bool: {
filter: [
{ term: { indicatorId: "SP.POP.TOTL" } },
{ terms: { country: ["United States", "Germany", "Japan"] } },
{ range: { year: { gte: "2018", lte: "2024" } } }
]
}
},
aggs: {
values: { stats: { field: "value" } },
countries: { terms: { field: "country", size: 6 } }
}
}
});
const subset = new Set(response.hits.hits.map((hit) => hit._id));
Once you have subset, you can treat it as the current slice of the data.
Build chart inputs from the slice
The charting library does not need to know anything about Querylight TS.
It only needs arrays of numbers, labels, and series data.
For example:
termsaggregation gives you category countsstatsgives you summary metricshistogramgives you numeric bucketsdate_histogramgives you time bucketssignificant_termsgives you characteristic vocabulary for a slice
That is enough to power:
- bar charts
- pies
- histograms
- time series
- heatmaps
- scatter plots
What the dashboard demo intentionally does not do
The demo is deliberately small in scope.
- The datasets are toy snapshots.
- The chart logic is illustrative rather than authoritative.
- The category heuristics are simple and may contain bugs.
- The goal is to show the pattern, not to ship a production BI stack.
That tradeoff is acceptable in a docs demo because the page is showing the indexing and slicing pattern, not promising production-grade analytics semantics.
When this approach is a good fit
- static dashboards bundled with a browser app
- internal tools over modest datasets
- API exploration and prototyping
- educational demos
- local-first tools where shipping raw records is acceptable
It is less suitable when:
- the dataset is too large for browser memory
- you need complex joins
- you need strict reporting guarantees
- the query cost belongs on a backend