Filtering & sorting
The GraphQL API page has a compact operator summary. This is the full reference: every operator by field type, combinators, sorting, system columns, and inherited fields.
The Where filter
A structured filter passed in the where argument:
query {
blogPosts(where: { status: { eq: "published" } }) {
id title
}
}
Each field in where is an object with an operator. The operator type
depends on the field type.
Operators by field type
| Field type | Operators |
|---|---|
| String, Text, Select | eq, neq, in, notIn, contains, startsWith, endsWith |
| Number | eq, neq, gt, gte, lt, lte, in |
| Boolean | eq |
| Date, DateTime | eq, neq, gt, gte, lt, lte |
| Content type (reference) | eq, in |
| Array (values) | contains, eq, in, notIn |
Examples:
# String: substring (case-insensitive)
where: { title: { contains: "vue" } }
# Number: greater than or equal
where: { view_count: { gte: 100 } }
# Boolean: equals
where: { featured: { eq: true } }
# Date: after (ISO 8601)
where: { published_at: { gt: "2026-01-01T00:00:00Z" } }
# Reference: equals (by ID)
where: { author: { eq: "550e8400-e29b-41d4-a716-446655440000" } }
# Array: contains (membership check)
where: { tags: { contains: "vue" } }
# Multiple values
where: { status: { in: ["draft", "review"] } }
Case sensitivity
contains, startsWith, endsWith are case-insensitive (ILIKE).
eq and neq on strings are case-sensitive. { title: { eq: "Vue" } }
won’t match "vue", but { title: { contains: "vue" } } will.
Empty lists
{ slug: { in: [] } } returns 0 records.
{ slug: { notIn: [] } } returns all records.
AND / OR / NOT combinators
Fields at the same level are implicitly AND-joined:
where: {
status: { eq: "published" } # AND
view_count: { gte: 100 } # AND
}
OR — a list of filters (any must match):
where: {
title: { contains: "Vue" }
OR: [
{ status: { eq: "draft" } }
{ view_count: { gte: 75 } }
]
}
# means: title contains "Vue" AND (status = draft OR view_count >= 75)
AND — a list (explicit AND within a group):
where: {
AND: [
{ status: { eq: "published" } }
{ author: { eq: "abc-123" } }
]
}
NOT — a single object (not a list):
where: {
NOT: { status: { eq: "draft" } }
}
# means: status ≠ draft
Combinators can be nested without limits.
Sorting (orderBy)
A list of field + direction pairs:
query {
blogPosts(orderBy: [{ field: PUBLISHED_AT, direction: DESC }]) {
title published_at
}
}
Multi-key sort — primary first, secondary second:
orderBy: [{ field: STATUS, direction: DESC }, { field: CREATED_AT, direction: ASC }]
Rules:
- Field names are uppercase (
PUBLISHED_AT,TITLE,CREATED_AT). - Direction:
ASC(default) orDESC. - Arrays cannot be sorted.
System columns
These columns exist on every type, regardless of schema:
| Column | Filter | Sortable |
|---|---|---|
id |
eq, in |
yes |
created_at |
date filter | yes |
updated_at |
date filter | yes |
where: { created_at: { gt: "2026-01-01T00:00:00Z" } }
orderBy: [{ field: CREATED_AT, direction: DESC }]
Inherited fields
If a type inherits fields via Extends, those fields are filterable and
sortable too. For example, blog-post inherits status from page — the
filter where: { status: { eq: "published" } } works.
What CANNOT be filtered
- JSON — arbitrary JSON cannot be compared.
- Polymorphic blocks (
blockson Page — Content type + array with no target type) — cannot be filtered or sorted. - An unknown field in
wheretriggers an error.
Search vs Where
The search argument is a substring scan across all field values and the
record’s ID (ILIKE on JSONB data and the id column). It combines with
where via AND:
blogPosts(search: "vue", where: { status: { eq: "published" } })
search scans everywhere; where filters by specific fields.
Count with a filter
blogPostsCount(where: { status: { eq: "published" } })
Returns the number of records matching the filter — for pagination.
Ignores limit and offset.
What’s next
- Webhooks — entity lifecycle events.
- GraphQL API — general overview of queries and mutations.
- Content types & fields — which field types exist.