Ordering And Cursors
Use explicit ordering, distinct, offset, custom order lists, and cursor pagination.
Explicit ordering
query {
products(order_by: { price: desc, id: asc }, limit: 5) {
id
price
}
}Always specify order_by when the order matters. SQL databases do not guarantee result ordering without it.
Ordering can target multiple columns, nested related tables, custom value lists, and null placement:
query Products($ids: [Int!]) {
products(
order_by: {
id: [$ids, "asc"]
price: { dir: desc, nulls: last }
}
where: { id: { in: $ids } }
limit: 5
) {
id
price
}
}Verified by
Example_queryWithOrderByList
tests/query_test.go:306Distinct and offset
query {
products(
limit: 5
offset: 10
distinct: [price]
order_by: { price: desc }
) {
id
price
}
}Verified by
Example_queryWithLimitOffsetOrderByDistinctAndWhere
tests/query_test.go:338Order by related tables
query {
products(order_by: { users: { email: desc }, id: desc }, limit: 5) {
id
price
}
}Verified by
Example_queryWithNestedOrderBy
tests/query_test.go:279Named cursors
Cursor fields let clients page through stable result sets. Request the cursor at the root level and pass it back through variables.
query ProductsPage($products_cursor: Cursor) {
products(
first: 10
after: $products_cursor
order_by: { id: asc }
) {
id
name
}
products_cursor
}{ "products_cursor": null }On the next page, set products_cursor to the returned value. Cursors are opaque; do not construct or parse them.
Verified by
Example_queryWithNamedCursorPagination
tests/query_test.go:2086Verified by
Example_queryWithNestedIndependentCursors
tests/query_test.go:2296Cursor rules
| Rule | Reason |
|---|---|
Use after: $products_cursor or before: $products_cursor. | Cursors must be variables, not string literals embedded in the query. |
Request products_cursor at the query root. | Cursor fields describe the page, not each row. |
Keep variable names cursor-shaped, such as products_cursor or cursor. | MCP cursor expansion only touches cursor variables. |
| Treat returned values as opaque. | GraphJin uses encrypted cursor payloads and a dynamic security prefix. |
Do not hardcode gj- or __gj-enc:. | gj-...: is generated from the active security context, and __gj-enc: is an MCP cache transport detail. |
Verified by
TestExpandCursorIDs_AlreadyEncrypted
serv/mcp_cursor_test.go:161Verified by
Example_queryWithNamedCursorInvalidVariable
tests/query_test.go:2152Backward pagination
query PreviousProducts($products_cursor: Cursor) {
products(
last: 10
before: $products_cursor
order_by: { id: asc }
) {
id
name
}
products_cursor
}Backward and forward pagination use the same root-level cursor field. Keep the ordering stable in both directions.
Verified by
Example_queryWithBackwardCompatibleCursor
tests/query_test.go:2091