Skip to content

Pagination and sorting

Use limit and offset, and always give an order_by that ends in a unique field such as id. Without one, rows can move between pages.

query Page($showroomId: uuid!, $offset: Int!) {
stocked_vehicles(
where: { showroom_stocked_vehicles: { showroom_id: { _eq: $showroomId } } }
order_by: [{ created_at: desc }, { id: asc }]
limit: 50
offset: $offset
) {
id
created_at
}
}

Page 1 is offset: 0, page 2 is offset: 50, and so on. Stop when a page returns fewer than limit rows.

Keep pages at 50 to 200 rows, fewer if you select large nested lists such as images. There is no hard maximum for API clients, but a smaller page is faster and less likely to time out. Requests without a token are capped at 500 vehicles.

Every list has an _aggregate twin that takes the same where. Ask for both in one request.

query PageWithTotal($showroomId: uuid!) {
stocked_vehicles(
where: { showroom_stocked_vehicles: { showroom_id: { _eq: $showroomId } } }
order_by: [{ created_at: desc }, { id: asc }]
limit: 50
) {
id
}
stocked_vehicles_aggregate(
where: { showroom_stocked_vehicles: { showroom_id: { _eq: $showroomId } } }
) {
aggregate {
count
min {
min_price
}
max {
min_price
}
}
}
}

order_by takes a field and a direction: asc, desc, asc_nulls_first, asc_nulls_last, desc_nulls_first or desc_nulls_last. Pass a list to sort by several fields in order.

Sort order_by
Newest first [{ created_at: desc }, { id: asc }]
Recently changed [{ updated_at: desc }, { id: asc }]
Cheapest first [{ min_price: asc_nulls_last }, { id: asc }]
Lowest kilometres [{ odometer: asc }, { id: asc }]
Newest model year [{ production_year: desc }, { id: asc }]
Dealer’s own order [{ featured_order: asc_nulls_last }, { random_order: asc }]

You can sort by a related object’s field, such as order_by: { location: { name: asc } }.

For a sync job, offsets are fragile: a vehicle that changes mid-sync moves position and you can miss one. Page by the last row you saw instead (keyset pagination).

query ChangedSince($showroomId: uuid!, $since: timestamptz!, $sinceId: uuid!) {
stocked_vehicles(
where: {
showroom_stocked_vehicles: { showroom_id: { _eq: $showroomId } }
_or: [
{ updated_at: { _gt: $since } }
{ updated_at: { _eq: $since }, id: { _gt: $sinceId } }
]
}
order_by: [{ updated_at: asc }, { id: asc }]
limit: 200
) {
id
updated_at
}
}

Start with since set to a time before your first sync and sinceId to 00000000-0000-0000-0000-000000000000. After each page, set both from the last row. Repeat until a page has fewer than 200 rows.