How to query records
Filter, search, sort, paginate, populate relations, and aggregate Datana records — server-side, with the client or raw HTTP.
TODO: translate to Indonesian. English source is authoritative; render it into Indonesian below.
Datana runs queries server-side, so you ask for exactly the slice you need instead of fetching everything and filtering in the browser. Each recipe below shows the @sawala/datana-client call and the equivalent raw request. All reads return only published records of public collections, with private fields stripped.
Set up the client once:
import { createDatanaClient } from '@sawala/datana-client'
const datana = createDatanaClient({
projectId: 'proj_xxx',
publicApiKey: 'pk_live_xxx',
})Filter
Pass an object for simple equality. Array fields (multi-select, many-relations) match by membership.
await datana.listRecords('startup', {
filter: { industry_sector: 'fintech', city: 'Palembang' },
})For ranges and lists, use the raw filter strings — field:in:a,b, field:gte:N, field:lte:N:
await datana.listRecords('startup', {
filter: ['total_employees:gte:10', 'industry_sector:in:fintech,edutech'],
})curl "https://api.sawala.cloud/public/datana/projects/proj_xxx/collections/startup/records?filter=total_employees:gte:10&filter=industry_sector:in:fintech,edutech" \
-H "X-API-Key: pk_live_xxx"filter is repeatable — every filter= is ANDed together.
Search by title
q is a case-insensitive search over the record title:
await datana.listRecords('startup', { q: 'bot' })Sort
Sort by any field — ascending by default, descending with a leading -. createdAt and id are always available:
await datana.listRecords('startup', { sort: '-createdAt' })
await datana.listRecords('startup', { sort: 'title' })Paginate
Reads are cursor-paginated. limit is 1–100 (default 25); follow pagination.nextCursor until it's empty:
let cursor: string | undefined
do {
const page = await datana.listRecords('startup', { limit: 100, cursor })
for (const record of page.items) handle(record)
cursor = page.pagination.nextCursor ?? undefined
} while (cursor)Populate relations
By default a relation field returns its reference. Ask Datana to inline the related record(s) with populate — name fields, or * for all relations:
await datana.getRecord('startup', '01J…', { populate: '*' })curl "https://api.sawala.cloud/public/datana/projects/proj_xxx/collections/startup/records/01J…?populate=founder,city" \
-H "X-API-Key: pk_live_xxx"Population happens at read time: a single relation inlines as an object (or null), a many-relation as an array.
Aggregate
Counts over published records — for stat cards and facet breakdowns. Works on single and array fields:
await datana.aggregate('startup', { count: true }) // { total: 501 }
await datana.aggregate('startup', { groupBy: 'province' }) // { groups: { 'dki-jakarta': 119, … } }
await datana.aggregate('startup', { distinct: ['city', 'industry_sector'] })curl "https://api.sawala.cloud/public/datana/projects/proj_xxx/collections/startup/aggregate?groupBy=industry_sector" \
-H "X-API-Key: pk_live_xxx"You can only filter and sort on public fields. Referencing a private field in filter or sort returns 400 PRIVATE_FIELD_NOT_QUERYABLE — private fields are reachable only with a secret key, server-side.
Introspect the schema
Fetch a collection's field definitions (types and select/relation options) — handy for building filter UIs:
const col = await datana.getCollection('startup')
col?.fields.forEach((f) => console.log(f.name, f.type))See the API reference for every parameter and the full response shape.
Your first collection
Model a typed collection in the dashboard, add and publish a few records, and read them from your code with the Datana client — in about fifteen minutes.
Datana API Reference
Datana's public API — base URL, key tiers, every read endpoint, the query parameters, and the one secret-key write.