Skip to main content

Paginate Large Result Sets

The Geo API uses cursor-based pagination (Relay-style). Every list query has a Connection variant that returns totalCount, pageInfo, and an opaque endCursor you pass back to fetch the next page.

The pattern — query

query Page($spaceId: UUID!, $typeId: UUID!, $after: Cursor) {
entitiesConnection(
spaceId: $spaceId
typeId: $typeId
first: 100
after: $after
) {
totalCount
pageInfo {
hasNextPage
endCursor
}
nodes {
id
name
}
}
}

The pattern — loop in JS

async function fetchAllProjects(spaceId: string) {
const all: Array<{ id: string; name: string | null }> = [];
let after: string | null = null;
do {
const data = await gql(query, { spaceId, typeId: PROJECT_TYPE_ID, after });
all.push(...data.entitiesConnection.nodes);
after = data.entitiesConnection.pageInfo.hasNextPage
? data.entitiesConnection.pageInfo.endCursor
: null;
} while (after);
return all;
}

What the response looks like

{
"data": {
"entitiesConnection": {
"totalCount": 657,
"pageInfo": {
"hasNextPage": true,
"endCursor": "WyJwcmltYXJ5X2tleV9hc2MiLFsiMDEyYjYwNTktNjY5NS00MTlmLWEwMTYtNTkwNzQ2ZTVhMjQzIl1d"
},
"nodes": [
{ "id": "00d1d0a8b36b48ae9cb531e09dfbd5be", "name": "AVP" },
{ "id": "012b60596695419fa016590746e5a243", "name": "ONNX Runtime" }
]
}
}
}

The endCursor is opaque base64 — don't parse it, just pass it back as after on the next request.

Loading interactive query runner…

Defaults & limits

ArgumentDefaultPractical max
firstvaries~5000 per request before timeouts; 100-500 is a good page size
afternullopaque cursor from previous endCursor

If you don't pass first, you get the server's default (sometimes only ~10). Always set first explicitly.

When pagination breaks

  • hasNextPage: false but totalCount > nodes returned: usually means you hit a query timeout server-side. Reduce first and try again.
  • endCursor returned as null while hasNextPage: true: a server quirk; treat as end of stream and log it.
  • Cursor expires: cursors are stateless; they don't expire. But if the underlying data changed dramatically between pages, items can shift between pages or be missed. For consistent reads of large collections, sort by createdAt or id to lock the order.

Notes

  • Most list fields have a Connection variantentitiesConnection, relationsConnection, etc. The non-connection variants (entities, relations) return a plain array and don't expose pagination.
  • totalCount is computed server-side and may be expensive on large connections. If you don't need it, skip selecting it for faster responses.