Querylight TS Demo

From Raw API Payloads to Browser Dashboards

A practical pattern for turning raw API responses into local-first dashboard interactions with Querylight TS and its JSON DSL.

Back to docs search

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.

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:

  1. download or collect raw records
  2. normalize them into a stable local shape
  3. index the fields you want to filter and aggregate
  4. derive subsets with queries
  5. 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:

  • terms aggregation gives you category counts
  • stats gives you summary metrics
  • histogram gives you numeric buckets
  • date_histogram gives you time buckets
  • significant_terms gives 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