Query
Gateway prefix: ${API_BASE}/data/query...
Backend controller: QueryController (/api/v1/query).
Request body fields (QueryRequest)
Core fields:
version: string, required, version (currently"1.0")from: string, required, main entity nameas: string, optional, alias for main entityselect: array, optional, selected fields (each item{ expr, alias? })joins: array, optional, joins based on relationsusing_relation: string, required, relation nameas: string, optional, join aliasjoin_type:inner | left | right | full(optional)on_filter: Predicate, optional, extra join conditionsselect:string[], optional, selected fields from joined entity
where: Predicate, optional, filter predicategroup_by:string[], optional, group-by fieldshaving: Predicate, optional, post-aggregation filterorder_by: array, optional, sorting (each item{ field, direction?, nulls? })direction:asc | descnulls:first | last
limit: integer, optionaloffset: integer, optionaltime: object, optional, time configuration (TimeConfig)time_field: string, optional, time field nametime_range: object, optional (start/end/preset)start/end: strings (timestamps)preset:last_7_days | last_30_days | last_60_days | last_90_days | last_180_days | this_month | last_month | this_year
grain:hour | day | week | month | quarter | yeartimezone: string, optional
params: object, optional, parameters (for view/template usage)use_view: string, optional, view to useuse_metrics:string[], optional, metrics to use (expanded by server into query expressions)
Predicate (generic filter predicate)
The Predicate model uses a unified structure, distinguished by type:
- Comparison:
type = "comparison"(or omittype, default is comparison)field: field nameop:eq | ne | gt | gte | lt | lte | in | not_in | like | ilike | between | is_null | is_not_nullvalue: any type (may be array/string/number/boolean)
- Logical:
type = "logical"op:and | orconditions:Predicate[]
- Not:
type = "not"op:notcondition:Predicate
Example (AND + OR):
{
"type": "logical",
"op": "and",
"conditions": [
{ "type": "comparison", "field": "status", "op": "eq", "value": "PAID" },
{
"type": "logical",
"op": "or",
"conditions": [
{ "type": "comparison", "field": "country", "op": "eq", "value": "US" },
{ "type": "comparison", "field": "country", "op": "eq", "value": "CA" }
]
}
]
}
Execute query
- POST
/query
Headers:
Authorization: Bearer <token>X-Tenant-Id: <tenantId>
Example (join + where + pagination):
curl -X POST \
"${API_BASE}/data/query" \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: tenant-abc123" \
-d '{
"version": "1.0",
"from": "orders",
"select": [
{ "expr": "id" },
{ "expr": "order_date" },
{ "expr": "total_amount", "alias": "amount" }
],
"where": { "type": "comparison", "field": "status", "op": "eq", "value": "PAID" },
"limit": 100,
"offset": 0
}'
Example response:
{"success": true, "data": {"status":"COMPLETED","data":[{"id":1,"order_date":"2024-09-01T00:00:00Z","amount":1200.5}],"totalRows":1}}
Validate query DSL
- POST
/query/validate - Permissions:
permitAll - Request body: same as QueryRequest
Explain query plan
- POST
/query/explain - Permissions:
admin/read - Header:
X-Tenant-Id - Request body: same as QueryRequest
Common error examples
- Unknown entity:
{"success": false, "message":"Entity 'unknown' not found", "errorCode":"QUERY_EXECUTION_FAILED"}
- Invalid field:
{"success": false, "message":"Field 'amountx' not found in 'orders'", "errorCode":"QUERY_EXECUTION_FAILED"}
- Invalid time range:
{"success": false, "message":"from must be before to", "errorCode":"QUERY_EXECUTION_FAILED"}