Skip to main content

Quickstart: Query API tour

A guided, read-only tour of the Query API: filters, field selection, nested loading, counting, metadata discovery, and one aggregation. Every call below is read-only and ran live against http://localhost:8080 — nothing in this quickstart writes or modifies data. See the Query API reference for the complete grammar; this page is the "try it in order" version.

Step count: 8 steps, all run live in this session.

Step 1: the simplest call — list with a field projection

curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/v1/query/seller?limit=5&fields=code,description"

Response:

{"total":12,"limit":5,"offset":0,"data":[{"code":"SELLER_FR","description":"France Shops"},{"code":"SELLER_US","description":"US Seller"},{"code":"MAIN_SELLER","description":"Demo Distributor"},{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"NETFLIX_MA","description":"Netflix Maroc"}]}

total is the full count of sellers on this stack (12 at the time this was captured), independent of limit.

Step 2: filter by a condition keyword

ne (not equals):

curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'filters={"ne code":"SELLER_FR"}' \
--data-urlencode 'fields=code,description' \
--data-urlencode 'limit=5' \
http://localhost:8080/api/v1/query/seller

Response:

{"total":11,"limit":5,"offset":0,"data":[{"code":"SELLER_US","description":"US Seller"},{"code":"MAIN_SELLER","description":"Demo Distributor"},{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"NETFLIX_MA","description":"Netflix Maroc"},{"code":"OPENAI_MA","description":"OpenAI Morocco"}]}

wildcardOr (substring match, auto-wraps the value in *...*):

curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'filters={"wildcardOr description":"Morocco"}' \
--data-urlencode 'fields=code,description' \
http://localhost:8080/api/v1/query/seller

Response:

{"total":2,"limit":100,"offset":0,"data":[{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"OPENAI_MA","description":"OpenAI Morocco"}]}

The full condition-keyword table has every verified operator: ranges, list membership, nested boolean groups, raw SQL fragments, and more.

Step 3: nested-entity loading

nested eagerly loads a related entity so you get its fields inline instead of just an id reference:

curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'filters={"code":"CUST_SARA"}' \
--data-urlencode 'fields=code,description,customerAccount.code,customerAccount.customer.code' \
--data-urlencode 'nested=customerAccount' \
http://localhost:8080/api/v1/query/billingAccount

Response:

{"total":1,"limit":100,"offset":0,"data":[{"code":"CUST_SARA","description":"Sara Benali","customerAccount":{"id":78,"code":"CUST_SARA","description":"Sara Benali","customer":{"id":77}}}]}

Without nested=customerAccount, customerAccount would come back as a bare {"id":78} reference. depth controls how many levels of nesting follow automatically past what nested names explicitly.

Step 4: count without fetching rows

curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/query/seller/count

Response:

{ "total": 12 }

Cheaper than a list call when you only need the number, and usable with the same filters parameter as the list endpoint.

Step 5: an aggregation — groupBy + SUM()/COUNT()

Putting a parenthesized aggregation expression in fields switches to the native aggregation path (see groupBy / having + aggregation for exactly which functions are recognized and why the space-separated form is a different, unrelated mechanism):

curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'fields=seller.code,SUM(amountWithoutTax),COUNT(id)' \
--data-urlencode 'groupBy=seller.code' \
--data-urlencode 'limit=5' \
http://localhost:8080/api/v1/query/walletOperation

Response:

{"total":2,"limit":5,"offset":0,"data":[{"seller.code":"PLUXEE_BE","SUM(amountWithoutTax)":-100.600000000000,"COUNT(id)":4746},{"seller.code":"EDENRED_FR","SUM(amountWithoutTax)":-48.000000000000,"COUNT(id)":1}]}

One row per group; total here means "2 groups," not "2 wallet operations."

Step 6: get a single record by id

curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/v1/query/seller/-3?fields=id,code,description"

Response:

{"data":{"id":-3,"code":"SELLER_FR","description":"France Shops"}}

Note the different envelope shape from list/count: a single data object, no total/limit/offset.

Step 7: discover what you can query (metadata endpoints)

List every queryable entity, or narrow to just the ones flagged @HugeEntity in the domain model:

curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/api/v1/query/entities?onlyHugeEntities=true"

Response (truncated to the first 10 of 48):

{"entities":["Access","AccountEntity","AccountOperation","Attribute","AuditLog","AutomatedPayment","AutomatedRefund","BillingAccount","CDR","ChargeTemplate", "...38 more"]}

Get the field-level schema (column names, FK/association flags) for one entity:

curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/api/v1/query/entities/seller"

Response (excerpt):

{
"javaName": "Seller",
"tableName": "crm_seller",
"fields": [
{"javaField": "billingTemplateName", "javaType": "String", "columnName": "billing_template_name"},
{"javaField": "generalLedger", "javaType": "GeneralLedger", "isFK": true, "columnName": "general_ledger_id", "isAssociation": true},
{"javaField": "legalText", "javaType": "String", "columnName": "legal_text"},
{"javaField": "legalType", "javaType": "String", "columnName": "legal_type"},
{"javaField": "seller", "javaType": "Seller", "isFK": true, "...": "..."}
]
}

Useful for building a query UI or a codegen step against entities you don't already know the shape of.

Step 8: same query, both dialects, byte-identical result

The GET dialect (used throughout this page) and the POST dialect (JSON body, same shape the legacy Generic API accepts) are the same implementation underneath. Same filter as step 2's wildcardOr example, sent as a POST body instead:

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"filters":{"wildcardOr description":"Morocco"},"genericFields":["code","description"]}' \
http://localhost:8080/api/v1/query/seller

Response (identical to step 2's GET call):

{"total":2,"limit":100,"offset":0,"data":[{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"OPENAI_MA","description":"OpenAI Morocco"}]}

Note the POST dialect keeps the legacy field name genericFields, not fields — see migrating from the Generic API for the complete field mapping between the two dialects.

Where to go next