I recently decided to use GraphQL for an API I am designing and ran into a point of confusion around how to implement cursor based pagination and summarized my findings here for others to use.
I started by checking out the reference documentation on paging:
Then I looked at GitHub’s alpha documentation: https://developer.github.com/early-access/graphql/
GitHub’s example shows their query using edges with same arguments of first and after. Overall it appeared to align directly with the guidelines from the official GraphQL docs. I decided I would try to implement paging for my API with cursors as well both for flexibility and as learning experiment.
Check out their API in the explorer:
(The GraphQL article makes cursors sound like a better solution in all cases, but if consumers of the API need a way to jump to a specific page without first making a request then you would need to use numerical offsets so make sure to evaluate and choose the best solution for you. In my case I chose to implement an API for both cursor based paging AND offset based paging so I can support both types of clients.)
Some key points to note about my implementation:
- Cursors are computed at run-time and are not stored with the entity.
- Cursors are base64 encoded result of other properties on the entity, typically just the id.
- When receiving a cursor in query it must be decoded from base64 back to the serialized properties and then used to find the associated item.
I created a pagination module to help consolidate this logic:
Then you can see how it’s used in a query named
(Note that in my sample above I find the index of the item with
id matching the id from the cursor. This is not recommended since it’s O(N) operation per query. This only works because I have the entire dataset in memory and it’s relatively small. In a more traditional service the data would be in database with ID fields indexed so they can be looked up directly in more performant maner. The key point is that you use the cursor to find the index of the item and then perform the same slicing technique you would normally do if your paging implementation was using standard skip/top or limit/offset.)
Now if I submit a query to my API such as:
I would get back:
Hope that brings some clarity for how to implement cursor based paging using GrahpQL. Let me know if you have any suggestions for improvement.