anfra docs
anfra SDK

Results

Read query state, rows, columns, paging and debug details

Each query exposes its current state and result as properties. Read them from your app.subscribe() callback.

State and errors

MemberDoes
query.state'idle', 'executing', 'success' or 'error'
query.resultThe result, or undefined before the first success. It is kept while the query re-executes, so you can render stale data under a spinner.
query.errorThe error when state === 'error'. See Errors.
query.setSort(sorts) / query.sortSets or reads the sort applied to the result. See Sorting.
query.hasMore / query.fetchMore()Pages through the result. See Paging.
query.select(rows, { fields? }) / query.selectedRowsSelects rows for cross-filtering, and reads which rows are selected.
query.abort()Cancels this query's in-flight request.
query.locate()Scrolls to the elements marked with this query and highlights them for a moment. See Page structure.
query.subscribe(fn)A change listener for just this query.

A page usually renders four states for each query: loading (executing), an error (error, showing query.error.message), a result with rows, and a result with no rows. Write the empty state in words, such as "No sales this week", rather than showing a bare zero.

Result shape

query.result.columns // [{ name, fieldName, modelId?, label, adhoc, isMeasure, aggregation? }]
query.result.rows    // [{ period: '2026-01-01', revenue: 1234.5 }, ...]
query.result.meta    // { numRows, page?, pageSize? }
  • Row keys are the aliases your AQL declared, such as period and revenue. They match column.name. column.fieldName is the underlying field, which you only need for debugging.
  • column.label is a display label. Prefer it for axis and header text.
  • column.adhoc marks a query-local expression, and column.isMeasure a measure. Neither can be used for cross-filtering.

Values are raw

Values are never formatted. Format numbers and dates yourself, for example with Intl.NumberFormat and toLocaleDateString.

  • Decimal values can arrive as strings ("97537.24") to keep their precision. Convert with Number(...) before charting a measure.
  • Dates arrive as ISO strings ("2025-06-01T00:00:00Z").

Log rows[0] on the first success to confirm the value types rather than guessing.

Sorting

query.setSort([{ field: 'revenue', direction: 'desc' }])
await app.execute()

field is a result column name, the alias from your AQL. direction is 'asc' (the default) or 'desc'. setSort([]) restores the sort in the AQL. Re-run with app.execute() after changing the sort.

Paging

A query without pageSize is not paged: it returns every row in one result.

Declare pageSize, a whole number of at least 1, to get the result a page at a time. Any other value throws a ValidationError. result.meta.page and result.meta.pageSize are present only for a paged query.

const orders = app.createQuery('orders', { dataset: 'sales', aql, pageSize: 100 })

if (orders.hasMore) await orders.fetchMore() // appends the next page
  • Unlike app.execute(), fetchMore() rejects on failure.
  • On a query with no pageSize, hasMore is always false, and fetchMore() rejects with a ValidationError.
  • A pivot query (rows { } / columns { }) can't be paged, so it must not declare pageSize.
  • To give the reader every row of a paged query as a file, use query.export() rather than building one from result.rows.

Debug information

result.debug holds:

  • executedAql: for an AQL query, your AQL with the reader's filters, sorts and date drills applied. When a control seems to do nothing, log this first.
  • executedSql: the SQL that ran on the warehouse.
  • executedAt: when it ran.
  • fromCache: whether the result came from cache.
console.log(query.result.debug?.executedAql)

Under anfra serve, the Inspect panel shows the same for every query. See Build a data app.

On this page