anfra docs
anfra SDK

Queries

Describe the data each view needs

A query object describes the data a view needs: a dataset and an AQL query. Your frontend reads its result and passes that data to a chart or other UI.

Creating a query

Here, the page redraws a revenue chart whenever the query updates. renderRevenueChart stands for your own drawing code, using any charting library:

const trend = app.createQuery('trend', {
  dataset: 'sales',
  aql: `explore {
    dimensions { period: date_trunc(orders.created_at, "month") }
    measures { revenue: revenue }
  }`,
})

app.subscribe(() => renderRevenueChart(document.querySelector('#chart'), trend.result))
await app.execute()

dataset is the dataset's fqn, as anfra show lists it and as Anfra.datasets keys it. aql is the query. An optional pageSize pages the result. See Paging.

Writing the AQL

The SDK does not run your AQL exactly as written. It sends it along with the reader's state: filter values, the cross-filter selection, sorts, date drills and the page. anfra applies that state to the query's explore { } before running it. Four rules follow:

  • Write an explore { }, optionally after query-local dimension and metric declarations. Any AQL expression works inside it. A pipe-style query (orders | select(...)) runs, but fails as soon as a reader touches a control, sort or date drill mapped to it.
  • Alias every dimension and measure, as in revenue: sum(orders.amount). The alias is the row key in query.result.rows and the name that sorts use.
  • Don't add a limit. Paging belongs to the SDK, through pageSize and fetchMore().
  • Keep the reader's choices out of the AQL text. Put static conditions in filters or having. A reader's choice is a control, never a value built into the query string.

Run each query with anfra query before putting it in a page, so mistakes show up in the terminal:

anfra query --dataset sales 'explore { dimensions { period: orders.created_at | month() } measures { revenue: revenue } }'

Running queries

app.execute() runs every query that needs updating. app.subscribe() registers a callback that runs when results change, so one subscription can redraw all views on the page.

app.execute() never rejects. It resolves { succeeded, failed }, and each failing query keeps its error on query.error. See Errors.

Queries respond to controls and to selections in other views through cross-filters. To read a query's rows, state and paging, see Results.

Adding queries later

You can declare queries, controls, control mappings and cross-filters at any time, including after execute(). The next execute() runs whatever is new.

A query's AQL is fixed once declared, and nothing can be removed. An app whose query changes with the reader's choices, such as a query builder or a pivot picker, declares a new query for each new AQL:

  • Give each one a unique name (run1, run2, and so on) and render only the latest.
  • Call query.abort() on the previous one if it may still be running.
  • Earlier queries keep their last result and stay idle, because execute() only re-runs a query whose own inputs changed.

Control mappings and cross-filters can't be removed either, so a control mapped onto an old query keeps re-running it. Give each new query its own controls, or start a fresh Anfra.createApp() and call abort() on the old app.

On this page