Query Language
JSONQL queries are JSON objects sent to the server via POST body or ?q= query parameter. This page covers the full grammar.
Basic Query
Section titled “Basic Query”{ "version": "1.0", "fields": ["id", "name", "email"], "where": { "status": { "eq": "active" } }, "sort": ["-created_at"], "limit": 20, "skip": 0}| Key | Type | Description |
|---|---|---|
version | "1.0" or "1.1" | Spec version. Defaults to latest if omitted. |
fields | string[] | Columns to return. Omit for all columns. |
where | object | Filter conditions |
sort | string or string[] | Prefix - for descending |
limit | integer | Maximum rows to return |
skip | integer | Number of rows to skip (offset). Alias: offset. |
include | string[] or object | Eager-load related resources |
Filters
Section titled “Filters”Comparison Operators
Section titled “Comparison Operators”{ "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.
Implicit Equality
Section titled “Implicit Equality”You can use a bare value as shorthand for eq:
{ "where": { "id": 1 }}This is equivalent to { "id": { "eq": 1 } }.
Null Filtering
Section titled “Null Filtering”Use eq and ne with null to filter for NULL/NOT NULL:
{ "where": { "deleted_at": { "eq": null } }}| Pattern | SQL |
|---|---|
{"eq": null} | IS NULL |
{"ne": null} | IS NOT NULL |
Set Operators
Section titled “Set Operators”{ "where": { "role": { "in": ["admin", "editor"] }, "status": { "nin": ["banned", "suspended"] } }}String Operators
Section titled “String Operators”{ "where": { "name": { "contains": "john" }, "email": { "ends": "@company.com" }, "title": { "starts": "How to" } }}String operators are case-insensitive by default.
Logical Operators
Section titled “Logical Operators”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" } } }}Field-to-Field Comparison
Section titled “Field-to-Field Comparison”Compare one field against another using the field wrapper:
{ "where": { "salePrice": { "gt": { "field": "basePrice" } } }}Sorting
Section titled “Sorting”Sort by one or more fields. Prefix with - for descending order:
{ "sort": ["-created_at", "name"]}Single-field shorthand:
{ "sort": "-created_at"}Pagination
Section titled “Pagination”{ "limit": 25, "skip": 50}SDKs enforce a configurable maxLimit (default 1000) to prevent unbounded queries.
Query Delivery
Section titled “Query Delivery”JSONQL queries can be sent in two ways:
POST Body (Primary)
Section titled “POST Body (Primary)”curl -X POST http://localhost:8080/users \ -H "Content-Type: application/json" \ -d '{"fields": ["id", "name"], "where": {"status": {"eq": "active"}}}'GET with Query Parameter
Section titled “GET with Query Parameter”URL-encode the JSON and pass it as the q parameter:
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"]}Relationships (Include)
Section titled “Relationships (Include)”Simple Include (v1.0)
Section titled “Simple Include (v1.0)”Request related resources as a flat list of relation names:
{ "from": "users", "fields": ["id", "name"], "include": ["posts", "profile"]}Advanced Include with Sub-Queries (v1.1)
Section titled “Advanced Include with Sub-Queries (v1.1)”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"] } }}Aggregations (v1.1)
Section titled “Aggregations (v1.1)”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.
Grouping (v1.1)
Section titled “Grouping (v1.1)”Group results by specific fields, typically combined with aggregations:
{ "groupBy": ["role"], "aggregate": { "count": { "count": "id" }, "avg_age": { "avg": "age" } }}Distinct (v1.1)
Section titled “Distinct (v1.1)”Select unique values:
{ "fields": ["category"], "distinct": true}Or specify which fields should be distinct:
{ "distinct": ["category", "status"]}Mutations
Section titled “Mutations”JSONQL also supports data mutations through the same JSON interface:
Create (INSERT)
Section titled “Create (INSERT)”{ "data": { "name": "Alice", "email": "alice@example.com" }}Update (PATCH)
Section titled “Update (PATCH)”{ "patch": { "name": "Bob" }, "where": { "id": { "eq": 1 } }}Delete (DELETE)
Section titled “Delete (DELETE)”{ "where": { "id": { "eq": 1 } }}The HTTP method determines the operation type: POST for create, PATCH/PUT for update, DELETE for delete.
Parser Options
Section titled “Parser Options”SDKs support server-side security limits to control what clients can query:
| Option | Description |
|---|---|
maxLimit | Maximum allowed limit value (default: 1000) |
maxNestingDepth | Maximum depth for nested where conditions |
allowedFields | Whitelist of fields clients can select |
allowedIncludes | Whitelist of relations clients can include |
These are configured server-side per adapter, not in the query itself. See the Architecture page for security details.
Full Reference
Section titled “Full Reference”For the complete specification, see: