GraphQL pagination with DynamoDB - Cursor specification

andyrichardsonn

Andy Richardson

Posted on April 28, 2021

GraphQL pagination with DynamoDB - Cursor specification

Note: This is a multi-part series; if you already know about relay pagination, feel free to skip to the next section.

Relay's Cursor Connection Specification has become the industry standard for pagination in GraphQL.

Whether you use a Relay, Apollo, or urql, the spec is supported by many client side libraries. This makes it an easy choice for those who don't want to implement their own client-side pagination logic (which is most of us).

πŸ’¬ Pagination grammar

Before we declare a pageable field, you're going to want to know about the following primitives.

Edge

An Edge is a container type which ensures that each item in a paginated response (node) is accompanied with a position in the collection (cursor).

type UserEdge {
  node: User!
  cursor: String!
}
Enter fullscreen mode Exit fullscreen mode

PageInfo

PageInfo is a metadata type which provides information about a pagination request.

type PageInfo {
  startCursor: String
  endCursor: String
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
}
Enter fullscreen mode Exit fullscreen mode

Connection

A Connection is a container type which aggregates Edge and PageInfo values and is the root type of any paginated response.

type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
}
Enter fullscreen mode Exit fullscreen mode

🎨 Creating a pageable field

Adding pagination support to a field is pretty straightforward. The field needs to take the following four arguments - first, after, last, and before (more on this later) - and return a Connection.

type Query {
  users(
    first: Int,
    after: String,
    last: Int, 
    before: String
  ): UserConnection!
}
Enter fullscreen mode Exit fullscreen mode

🏎️ Page traversal

Paginated fields take four arguments which can be divided into two distinct groups.

Forward pagination

Forward pagination is the process of fetching pages in a collection from top-to-bottom.

Arguments

The arguments to use forward pagination are:

  • first - the number of items to return (required)
  • after - a cursor resembling the position in the collection to start from (optional)

Forward pagination

Retrieval

As you can see above, the first paginated query will include only the first argument. This tells the backend to start at the top of the collection.

For subsequent queries the after argument is used to instruct the backend where to continue from. This value corresponds to the cursor of the last edge on the previous response.

Backward pagination

Backward pagination is the process of fetching pages in a collection from bottom-to-top.

Arguments

The arguments to use backward pagination are:

  • last - the number of items to return (required)
  • before - a cursor resembling the position in the collection to start from (optional)

Backward pagination

Retrieval

This second example might look familiar - that's because it's almost identical to forward pagination.

Just like with forward pagination, the page size is sent in isolation for the first query (this time last rather than first).

Again, for subsequent queries, a cursor is provided to instruct the backend where to continue from (this time before rather than after). This value corresponds to the cursor on the first (as opposed to last) edge on the previous response.

It's important to note, the sort order in which edges are returned is the same regardless of whether forward or backward pagination is used.


Thanks for reading!

If you enjoyed this post, be sure to react πŸ¦„ or drop a comment below with any thoughts πŸ€”.

You can also hit me up on twitterβ€Š-β€Š@andyrichardsonn

Disclaimer: All thoughts and opinions expressed in this article are my own.

πŸ’– πŸ’ͺ πŸ™… 🚩
andyrichardsonn
Andy Richardson

Posted on April 28, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related