API

Sort, filter and search

One JSON query to sort, filter, search, pick fields, join related records and count.

List records takes an optional query parameter: a JSON object, URL-encoded. GraphQL accepts the same object. The examples use a contacts collection and reuse base and token from earlier pages.

Send a query

GET /api/v1/workspaces/:workspace/collections/:collection/records

curl --get 'http://127.0.0.1:8080/api/v1/workspaces/YOUR_WORKSPACE_ID/collections/YOUR_COLLECTION_ID/records' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --data-urlencode 'limit=20' \
  --data-urlencode 'query={"filters":[{"field":"is_customer","operator":"eq","value":true}],"sort":[{"field":"deal_value","descending":true}],"fields":["full_name","deal_value"],"meta":["filter_count"]}'
const query = {
	filters: [{ field: 'is_customer', operator: 'eq', value: true }],
	sort: [{ field: 'deal_value', descending: true }],
	fields: ['full_name', 'deal_value'],
	meta: ['filter_count']
};
const params = new URLSearchParams({ limit: '20', query: JSON.stringify(query) });
const res = await fetch(`${base}/records?${params}`, {
	headers: { Authorization: `Bearer ${token}` }
});
const { data, meta } = await res.json();
query, _ := json.Marshal(map[string]any{
	"filters": []map[string]any{{"field": "is_customer", "operator": "eq", "value": true}},
	"sort":    []map[string]any{{"field": "deal_value", "descending": true}},
	"fields":  []string{"full_name", "deal_value"},
	"meta":    []string{"filter_count"},
})
params := url.Values{"limit": {"20"}, "query": {string(query)}}
req, _ := http.NewRequest("GET", base+"/records?"+params.Encode(), nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
import json

query = {
    "filters": [{"field": "is_customer", "operator": "eq", "value": True}],
    "sort": [{"field": "deal_value", "descending": True}],
    "fields": ["full_name", "deal_value"],
    "meta": ["filter_count"],
}
res = requests.get(
    f"{base}/records",
    headers={"Authorization": f"Bearer {token}"},
    params={"limit": 20, "query": json.dumps(query)},
)

The counts you ask for appear in meta, next to pagination:

{
	"pagination": { "limit": 20, "offset": 0, "count": 20, "has_more": true },
	"filter_count": 50,
	"total_count": 126
}

Options

OptionWhat it does
filtersUp to 8 conditions. All must match. Each has field, operator and value.
sortUp to 3 fields, each with field and descending. Order is stable and nulls come last.
fieldsReturn only these fields. Leave it out to get every field.
searchEvery word must match at least one search field.
search_fieldsFields to search. Without it, every string and text field is searched.
joinsUp to 3 lookups into other collections, up to 8 fields each.
metatotal_count counts all rows, filter_count counts rows that match. Off unless asked, to keep reads fast.

Filter operators

OperatorMeansWorks on
eqequalsevery type except JSON
nedoes not equalevery type except JSON
gt, gtegreater than, or equalnumbers and dates
lt, lteless than, or equalnumbers and dates
containscontains the textstring and text
null, not_nullis empty, is not empty (no value)every type, including JSON

Booleans only support eq and ne. Dates are compared in UTC.

Join related records

A join reads fields from a matching record in another collection. collection is the other collection’s ID. target_field is the field to match on; leave it out to match on id.

{
	"joins": [
		{
			"field": "company",
			"collection": "19c2b406b68223b96809deaead4b34e9",
			"target_field": "name",
			"fields": ["industry"]
		}
	]
}

The joined fields appear under _related:

{
	"full_name": "Citra Lestari",
	"company": "FinHub",
	"_related": { "company": { "industry": "Fintech" } }
}

If nothing matches, the value is null. Joins only read; they create no foreign keys.

Exports

Export accepts filters, sort and search, and always returns every field. fields and meta are rejected there.

Troubleshooting

  • 400 invalid_input: check the limits (8 filters, 3 sorts, 3 joins, 8 fields per join), the operator name and the value type.
  • A join is null: the source value must equal the target field in the other collection.
  • No counts in meta: add total_count or filter_count to meta.

Next steps

Buy me a coffee