Skip to content

Filtering ​

http
GET /books?filter[title][eq]=Dune

The general form is filter[<field>][<operator>]=<value>. Multiple filter[...] params AND together, and multiple operators on the same field also AND together:

http
GET /books?filter[pages][gte]=200&filter[pages][lt]=500

Operators ​

OperatorWire tokenExample
Equalseqfilter[status][eq]=active
Not equalsnefilter[status][ne]=banned
Greater/equalgt / gtefilter[age][gte]=18
Less/equallt / ltefilter[age][lt]=65
In listinfilter[status][in]=active,pending
Not in listnotInfilter[role][notIn]=bot,test
Likelikefilter[name][like]=%25john%25
Case-insensitive likeilikefilter[name][ilike]=%25john%25
Betweenbetweenfilter[createdAt][between]=2026-01-01,2026-06-01
Is nullisNullfilter[deletedAt][isNull]=true
Is not nullisNotNullfilter[deletedAt][isNotNull]=true

Wire tokens are exact case, so gte works but GTE does not. A misspelled or wrong-case operator returns a 400 instead of being silently ignored.

like and ilike never auto-wrap wildcards: pass % yourself, and escape any literal % or _ in the value with a backslash. Both apply to string columns only.

in and notIn also accept the repeated-key form instead of a comma list:

http
GET /books?filter[status][in][]=active&filter[status][in][]=pending

between takes exactly two comma-separated bounds. isNull and isNotNull are boolean-valued: isNull=false means the same thing as isNotNull=true, so pick whichever reads better.

Which fields you can filter on ​

Only fields on the entity's filter.fields allowlist can be filtered on. See Allowed for how to configure that list. Filtering on anything outside it returns a 400, never a silent no-op.

OR, NOT, and nested logic ​

The same bracket grammar covers or and not, and it can nest arbitrarily deep, up to filter.limits.maxDepth (default 3):

http
GET /books?filter[or][0][author][eq]=Tolkien&filter[or][1][author][eq]=Herbert
GET /books?filter[not][status][eq]=banned

For anything the bracket grammar gets awkward at, filter also accepts one JSON-encoded value as a full-power escape hatch. It parses into the same filter tree as the bracket form. If both are present on a request, they AND together:

http
GET /books?filter={"or":[{"author":{"eq":"Tolkien"}},{"not":{"status":{"eq":"banned"}}}]}

Filtering across relations ​

Relation-path filters use dot notation and restrict root rows, without loading the related collection:

http
GET /books?filter[author.country][eq]=UK

This never filters what's inside an included relation. It only decides which root rows come back.

Limits ​

Every request is guarded by limits, configurable per scope:

  • filter.limits.maxDepth (default 3) caps how deeply or/not can nest.
  • filter.limits.maxInValues (default 100) caps in/notIn array length.
  • filter.limits.maxLikePatternLength (default 200) caps like/ilike pattern length.
  • pagination.maxLimit (default 100) caps page size.

If a request breaks several of these at once (filter, sort, select, pagination), Kavo collects the violations and reports them together in a single response. See Errors.