Skip to content

Query Language

JSONQL queries are JSON objects sent to the server via POST body or ?q= query parameter. This page covers the full grammar.

{
"version": "1.0",
"fields": ["id", "name", "email"],
"where": { "status": { "eq": "active" } },
"sort": ["-created_at"],
"limit": 20,
"skip": 0
}
KeyTypeDescription
version"1.0" or "1.1"Spec version. Defaults to latest if omitted.
fieldsstring[]Columns to return. Omit for all columns.
whereobjectFilter conditions
sortstring or string[]Prefix - for descending
limitintegerMaximum rows to return
skipintegerNumber of rows to skip (offset). Alias: offset.
includestring[] or objectEager-load related resources
{
"where": {
"age": { "gte": 21 },
"status": { "eq": "active" },
"score": { "lt": 100 }
}
}

All comparison operators: eq, ne, gt, gte, lt, lte.

Multiple conditions at the same level are combined with implicit AND.

You can use a bare value as shorthand for eq:

{
"where": { "id": 1 }
}

This is equivalent to { "id": { "eq": 1 } }.

Use eq and ne with null to filter for NULL/NOT NULL:

{
"where": {
"deleted_at": { "eq": null }
}
}
PatternSQL
{"eq": null}IS NULL
{"ne": null}IS NOT NULL
{
"where": {
"role": { "in": ["admin", "editor"] },
"status": { "nin": ["banned", "suspended"] }
}
}
{
"where": {
"name": { "contains": "john" },
"email": { "ends": "@company.com" },
"title": { "starts": "How to" }
}
}

String operators are case-insensitive by default.

Use and, or, and not to compose complex filters:

{
"where": {
"and": [
{ "age": { "gte": 18 } },
{
"or": [
{ "role": { "eq": "admin" } },
{ "email": { "ends": "@company.com" } }
]
}
]
}
}

The not operator negates a condition:

{
"where": {
"not": { "status": { "eq": "active" } }
}
}

Compare one field against another using the field wrapper:

{
"where": {
"salePrice": { "gt": { "field": "basePrice" } }
}
}

Sort by one or more fields. Prefix with - for descending order:

{
"sort": ["-created_at", "name"]
}

Single-field shorthand:

{
"sort": "-created_at"
}
{
"limit": 25,
"skip": 50
}

SDKs enforce a configurable maxLimit (default 1000) to prevent unbounded queries.

JSONQL queries can be sent in two ways:

Terminal window
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"fields": ["id", "name"], "where": {"status": {"eq": "active"}}}'

URL-encode the JSON and pass it as the q parameter:

Terminal window
curl "http://localhost:8080/users?q=%7B%22fields%22%3A%5B%22id%22%2C%22name%22%5D%7D"

Equivalent to:

GET /users?q={"fields":["id","name"]}

Request related resources as a flat list of relation names:

{
"from": "users",
"fields": ["id", "name"],
"include": ["posts", "profile"]
}

Apply filters, sorting, field selection, and limits to included relations:

{
"include": {
"posts": {
"fields": ["id", "title", "slug"],
"where": { "published": { "eq": true } },
"sort": "-created_at",
"limit": 5
},
"profile": {
"fields": ["bio", "avatar_url"]
}
}
}

Perform calculations on datasets:

{
"aggregate": {
"total_users": { "count": "id" },
"average_age": { "avg": "age" },
"max_score": { "max": "score" },
"total_revenue": { "sum": "amount" },
"oldest": { "min": "birth_date" }
}
}

Supported functions: count, sum, avg, min, max.

Group results by specific fields, typically combined with aggregations:

{
"groupBy": ["role"],
"aggregate": {
"count": { "count": "id" },
"avg_age": { "avg": "age" }
}
}

Select unique values:

{
"fields": ["category"],
"distinct": true
}

Or specify which fields should be distinct:

{
"distinct": ["category", "status"]
}

JSONQL also supports data mutations through the same JSON interface:

{
"data": { "name": "Alice", "email": "alice@example.com" }
}
{
"patch": { "name": "Bob" },
"where": { "id": { "eq": 1 } }
}
{
"where": { "id": { "eq": 1 } }
}

The HTTP method determines the operation type: POST for create, PATCH/PUT for update, DELETE for delete.

SDKs support server-side security limits to control what clients can query:

OptionDescription
maxLimitMaximum allowed limit value (default: 1000)
maxNestingDepthMaximum depth for nested where conditions
allowedFieldsWhitelist of fields clients can select
allowedIncludesWhitelist of relations clients can include

These are configured server-side per adapter, not in the query itself. See the Architecture page for security details.

For the complete specification, see: