Querying
Filter, sort, paginate, and select fields via query parameters.
All query parameters are parsed with qs and support nested bracket syntax. The query engine translates them into SQL, aware of each field’s storage type (column, JSONB, or relation).
Filtering
Basic equality
GET /api/posts?filter[status]=published
Operators
Use operator keys for comparisons:
GET /api/posts?filter[publishedAt][$gte]=2025-01-01
GET /api/posts?filter[title][$contains]=design
GET /api/posts?filter[priority][$in][]=high&filter[priority][$in][]=medium
Supported operators
| Operator | SQL | Notes |
|---|---|---|
$eq | = | Implicit when no operator specified |
$neq | != | |
$gt | > | |
$gte | >= | |
$lt | < | |
$lte | <= | |
$contains | ILIKE '%x%' | Case-insensitive |
$startsWith | ILIKE 'x%' | Case-insensitive |
$endsWith | ILIKE '%x' | Case-insensitive |
$in | IN (...) | Bracket array syntax: filter[field][$in][]=a&filter[field][$in][]=b |
$notIn | NOT IN (...) | Bracket array syntax: filter[field][$notIn][]=a&filter[field][$notIn][]=b |
$exists | IS [NOT] NULL | true or false |
$glob | Pattern match | For slug hierarchy |
OR conditions
GET /api/posts?filter[$or][0][status]=draft&filter[$or][1][status]=review
Relation traversal
Filter by fields on related entities using dot notation:
GET /api/posts?filter[author.name]=Pedro
This generates a subquery that joins to the authors table.
JSONB path filtering
For fields stored as JSONB, dot notation extracts nested values:
GET /api/posts?filter[body.title][$contains]=hello
Translates to: body->>'title' ILIKE '%hello%'
Top-level JSONB fields (without dot notation) only support
$exists. Use dot notation for path-specific queries (e.g.,filter[body.title][$contains]=hello).
Slug glob patterns
The $glob operator supports path-based filtering for hierarchical slugs:
# Direct children of /blog/2025/
GET /api/pages?filter[slug][$glob]=/blog/2025/*
# All descendants under /blog/
GET /api/pages?filter[slug][$glob]=/blog/**/*
SQL translation:
-- /blog/2025/* → direct children only
WHERE slug LIKE '/blog/2025/%' AND slug NOT LIKE '/blog/2025/%/%'
-- /blog/**/* → all descendants
WHERE slug LIKE '/blog/%'
Sorting
Use the sort parameter with comma-separated fields. Prefix with - for descending:
GET /api/posts?sort=-publishedAt,title
Maps to: ORDER BY published_at DESC, title ASC
Default sort is createdAt DESC.
JSONB fields cannot be used for sorting.
Field selection
Control which fields appear in the response:
GET /api/posts?fields=title,slug,author
The id field is always included. Omit fields to return all fields.
Pagination
GET /api/posts?limit=10&offset=20
limit: 1–100 (default: 20)offset: 0+ (default: 0)
List responses include pagination metadata:
{
"data": [...],
"meta": {
"total": 42,
"limit": 10,
"offset": 20
}
}
Relation resolution
See the Relations guide for details on the resolve parameter.
GET /api/posts?resolve[author]=name,bio&resolve[tags]=*
Combining parameters
All parameters compose freely:
GET /api/posts?filter[status]=published&sort=-publishedAt&limit=5&fields=title,slug&resolve[author]=name
Previous
Relations
Next
Permissions