Skip to content

Pagination Explained

When you're dealing with thousands of products and hundreds of stores, you can't just fetch everything at once. Pagination lets you fetch data in chunks.

LCBO.dev uses cursor-based pagination (the Relay spec). Here's what you need to know.

Why Cursors Instead of Page Numbers

Most REST APIs use offset pagination - you ask for "page 2" or "skip 20, take 20". Simple, but it breaks down:

  • If new data gets added while you're paginating, you see duplicates
  • If data gets removed, you skip items

Cursors fix this. Instead of "give me items 20-40", you say "give me items after this point". The cursor marks a stable position in the dataset, so even if data changes around you, you won't see duplicates or miss items.

The tradeoff: you can't jump directly to page 47. You have to page through sequentially. For most apps (infinite scroll, "load more" buttons), this isn't a problem.

The Connection Pattern

Every paginated query returns a "connection" with three parts:

edges - Array of results. Each edge has:

  • node - The actual item (product, store, etc.)
  • cursor - An opaque string marking this item's position

pageInfo - Tells you where you are:

  • hasNextPage - Are there more items after this page?
  • hasPreviousPage - Are there items before this page?
  • endCursor - Pass this as after to get the next page
  • startCursor - Pass this as before to get the previous page

totalCount - Total items matching your query (across all pages). Handy for showing "1-20 of 1,847 results".

How to Paginate

Forward: Use first to set page size, after to continue from where you left off. Check hasNextPage to know when you're done.

Backward: Use last and before instead. Check hasPreviousPage.

When filters change, reset your cursor - you're starting a new result set.

Page Sizes

Recommended sizes:

  • Product lists: 20-50
  • Search results: 15-25
  • Store locators: 10-25
  • Dropdowns: 50-100

Maximum is 100 items per request.

About Those Cursors

Cursors look like Y3Vyc29yOjIw - that's just base64. Don't try to decode or construct them yourself. The format could change anytime. Just pass them back exactly as you received them.

If a cursor becomes invalid (maybe the underlying data was deleted), the API returns an error. Handle it by resetting to page 1.

Can't I Just Jump to Page 50?

Not directly. Cursor pagination optimizes for sequential access (infinite scroll, next/prev) over random access.

If you need page numbers in your UI, store cursors as users navigate forward. Use totalCount with your page size to calculate total pages. Or consider whether you actually need that UX - most users browse sequentially anyway.

Quick Reference

ForwardBackward
first: 20last: 20
after: endCursorbefore: startCursor
Check hasNextPageCheck hasPreviousPage

Next Steps