Skip to main content

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 name
  • as: string, optional, alias for main entity
  • select: array, optional, selected fields (each item { expr, alias? })
  • joins: array, optional, joins based on relations
    • using_relation: string, required, relation name
    • as: string, optional, join alias
    • join_type: inner | left | right | full (optional)
    • on_filter: Predicate, optional, extra join conditions
    • select: string[], optional, selected fields from joined entity
  • where: Predicate, optional, filter predicate
  • group_by: string[], optional, group-by fields
  • having: Predicate, optional, post-aggregation filter
  • order_by: array, optional, sorting (each item { field, direction?, nulls? })
    • direction: asc | desc
    • nulls: first | last
  • limit: integer, optional
  • offset: integer, optional
  • time: object, optional, time configuration (TimeConfig)
    • time_field: string, optional, time field name
    • time_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 | year
    • timezone: string, optional
  • params: object, optional, parameters (for view/template usage)
  • use_view: string, optional, view to use
  • use_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 omit type, default is comparison)
    • field: field name
    • op: eq | ne | gt | gte | lt | lte | in | not_in | like | ilike | between | is_null | is_not_null
    • value: any type (may be array/string/number/boolean)
  • Logical: type = "logical"
    • op: and | or
    • conditions: Predicate[]
  • Not: type = "not"
    • op: not
    • condition: 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"}