Pagination
Pagination
Queries that return lists, like cars, customers and visits, are paginated with cursors. Each returns a page type, such as CarPage, with two fields:
nodes: the records on this page.pageInfo: where this page sits in the list, and the cursors to get the pages around it.
Pagination arguments go inside the query’s input object, alongside its filters:
query Cars($input: CarsQueryInput) {
cars(input: $input) {
nodes {
id
plate
}
pageInfo {
hasNextPage
endCursor
}
}
}
{ "input": { "first": 25 } }
Arguments
| Argument | Meaning |
|---|---|
first |
Return up to this many records from the start of the range. |
after |
Start after the record with this cursor. |
last |
Return up to this many records from the end of the range. |
before |
End before the record with this cursor. |
Use first with after to page forward, and last with before to page backward. Don’t mix the two directions in one request.
- With no
firstorlast, a page has 20 records. - A page can have at most 100 records.
Larger pages cost more against your rate limit, because a page’s selection is multiplied by its page size.
Page forward
Request the first page, then pass each page’s pageInfo.endCursor as after until hasNextPage is false:
async function fetchAllCars(accessToken) {
const cars = [];
let after = null;
do {
const response = await fetch("https://api.rollerup.tech/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
query: `query Cars($input: CarsQueryInput) {
cars(input: $input) {
nodes { id plate }
pageInfo { hasNextPage endCursor }
}
}`,
variables: { input: { first: 100, after } },
}),
});
const { data, errors } = await response.json();
if (errors) throw new Error(errors[0].message);
cars.push(...data.cars.nodes);
after = data.cars.pageInfo.hasNextPage ? data.cars.pageInfo.endCursor : null;
} while (after);
return cars;
}
Keep the filters the same on every request. A cursor marks a position in one particular list, so it only works with the filters that produced it.
Page backward
To read from the end of the range, use last. For the page before, pass pageInfo.startCursor as before, and continue while hasPreviousPage is true. Records within each page keep the list’s normal order.
pageInfo
| Field | Meaning |
|---|---|
hasNextPage |
Paging forward: whether there are more records after this page. |
hasPreviousPage |
Paging backward: whether there are more records before this page. |
startCursor |
Cursor of the first record on this page, or null if the page is empty. |
endCursor |
Cursor of the last record on this page, or null if the page is empty. |
Each flag is only checked in the direction you’re paging. Paging forward, hasPreviousPage just tells you whether you passed after. Paging backward, hasNextPage just tells you whether you passed before.
Cursors
Cursors are opaque strings. Treat them as tokens: pass back exactly what the API gave you, and don’t build or parse your own. Their format can change.
Each list has a fixed order, newest first unless the query’s description says otherwise, with ties broken by creation time and then ID. A cursor marks a position in that order, not a snapshot of the data:
- Adding or deleting records while you page won’t make you skip a record or see one twice.
- New records sort to the start of a newest-first list, so a forward walk that’s already underway won’t include them. Start again from the first page to pick them up.
Errors
These come back as GraphQL errors in the errors array:
| Message | Cause |
|---|---|
Invalid cursor |
The cursor wasn’t one the API issued, or it was altered. |
Cannot specify both 'first' and 'last' limits at the same time. |
Both first and last were set. |
Cannot specify both 'after' and 'before' cursors at the same time. |
Both after and before were set. |
Cannot use 'first' with 'before'. Use 'last' with 'before' for backward pagination. |
The two directions were mixed. |
Cannot use 'last' with 'after'. Use 'first' with 'after' for forward pagination. |
The two directions were mixed. |
Cannot request more than 100 items per page. |
first or last was over 100. This one has the VALIDATION code. |