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

OperatorSQLNotes
$eq=Implicit when no operator specified
$neq!=
$gt>
$gte>=
$lt<
$lte<=
$containsILIKE '%x%'Case-insensitive
$startsWithILIKE 'x%'Case-insensitive
$endsWithILIKE '%x'Case-insensitive
$inIN (...)Bracket array syntax: filter[field][$in][]=a&filter[field][$in][]=b
$notInNOT IN (...)Bracket array syntax: filter[field][$notIn][]=a&filter[field][$notIn][]=b
$existsIS [NOT] NULLtrue or false
$globPattern matchFor 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