How to Work with GraphQL Pagination

graphql pagination

This is a complimentary blog post to a video from the ShopifyDevs YouTube channel. In this article, Chuck Kosman, a Launch Engineer at Shopify Plus, dives deeper into the essential features you should know when working with GraphQL, focusing on GraphQL pagination. This is the fourth video in a five-part series of tutorials designed to improve your knowledge of the language. You’ll learn about the pageInfo property, how to use the after argument, and explore the advantages of working with cursor-based pagination.

Note: All images in this article are hyperlinked to the associated timestamps of the YouTube video, so you can click on them for more information.

Introducing a new product query

We’ll be picking up where we left off in part three of the tutorial where we looked at working with GraphQL fragments as a way to recycle fields. You can also step back and read up on what is GraphQL if you’d like an overview of getting started and the language itself.

Now, I have totally removed what I was working with in the last tutorial, and I've written a new query, which I called, using operation names, query ThreeProducts. All this does is retrieve the first three products in the store. So, what I'm going to do here is I'll just press play and verify that this works.

graphql pagination: gif taken from youtube video

We get these three apparel items in my test store.

You might be wondering, well, If I get the first three, how do I get anything beyond the first three? How do I start working with pages on GraphQL?

Just as a recap, If you read our article on getting started with GraphQL, you would know that when you request anything that's pluralized, Shopify's API is implementing a connection specification. Essentially, when you get anything plural, the type that you get back is a list of edges that is called a connection.

"When you request anything that's pluralized, Shopify's API is implementing a connection specification."

Let’s look at that ProductConnection

graphql pagination: gif taken from youtube video

So, I got the list of edges, that's why I had to write edges and node. Then at the end of node is where I can actually get the title and description

Introducing the PageInfo property

There is another property here on the connections called PageInfo, and it returns a PageInfo type that is non-nullable.

Right at the same level as edges, I'm going to get that field as well and let's actually inspect that type to see what it tells me.

graphql pagination: screenshot of pageInfo taken from the video

It's two booleans. One is called hasNextPage and the other, hasPreviousPage. So let's try this out. I'm going to press play again.

graphql pagination: screenshot of pageInfo taken from video

This is saying that hasNextPage is true. So there is another page of results, and that means that there's at least one product that hasn't been captured in this particular page of results. 

Now, because this is the first page, there's not a page before this one. Therefore, hasPreviousPage is false. 

So how do we actually get at that next page of results? What do I look to?

You might also like: How to Build a Shopify App in One Week.

Using relative cursor-based pagination 

The way that Shopify has implemented GraphQL is relying on the connections specification that's part of the relay library. Many other GraphQL schemas have chosen to adopt cursor-based pagination over offset-based pagination. There are very good performance reasons behind the scenes for doing this.

"The way that Shopify has implemented GraphQL is relying on the connections specification that's part of the relay library."

For now, know that the way that cursor-based pagination works, is that each edge essentially has an index to say this is a unique identifier of this particular edge. Not a unique identifier of the node, but the actual edge itself, because connections return a list of edges.

Let's jump to the schema definition of a product edge to explore this a little further. In addition to the node that I get back on that edge, the other important property here is the string.

So I'm actually going to say, tell me about the cursor as well. 

graphql pagination: gif taken from youtube video

Now, I get a lot more data back in this array of edges. Each edge has this opaque, unique identifier of the cursor.

The cursor is kind of like the position of that edge in the list.

Let’s say I needed to get the next three pages of results. What I would do is I'm going to look at this product's query and I'm going to take a look at the actual field itself to see what arguments I can supply.

So I'm going to click on the actual field products here, not the type that gets returned, but products, and these are lists of the arguments that I can do.

graphql pagination: gif taken from youtube video

Now, I'm only using first here, that's kind of the most basic one. But this goes along with another argument that I can use. What I'm going to say is, the last cursor that I retrieved is this string

Employing the after argument

So what we’re going to do, just for simplicity's sake, is we’re going to copy that string and I'm going to supply a second argument to say after this particular cursor

graphql pagination: screenshot of syntax taken from youtube video

Now, let's just take a look at the products here. I got back Ocean Blue Shirt, Classic Varsity Top, and Yellow Wool Jumper.

If I’m issuing a request for the next page of results, I should expect that none of these show up. All my products are titled uniquely, so we’re saying, “Give me the first three products after this last cursor.”

I'm going to press play.

graphql pagination: gif taken from youtube video

The titles that I get back here are none that matched the last set of results (Striped Silk Blouse, Floral White Top, and Classic Leather Jacket). And importantly, the page info has changed too.

Apparently there's still another set of results to be had here and there is now a previous page. So the page information is telling me that there is both a page after this, and a page before this.

You might also like: How to Use GraphQL Fragments.

I'm not sure how many products I have in the store, so this will actually be a good test.

Let's say I have ten products in the store and I change the argument to first:10 and I just get the first page of results. It looks like I have another page.

If I were to increase this to something like fifty products and I said first:50, let’s see what happens when I press play.

graphql pagination: gif taken from youtube video

Apparently there aren't actually 50 products in the store. It's either 50 or less. Now both hasNextPage and hasPreviousPage are False.

I've been using first and after, but if you wanted to go in reverse order, there's a couple of different things you could do. You could say you want the last elements from the list and you could say you want the last elements before some cursor. You could also reverse the listing of these things so that instead of listing them in the order of created at ascending, you'd get them in order of created at descending. So you'd start with, say, the product that was created last. 

You might also like: How to Use GraphQL Aliases.

Nested GraphQL pagination

Pagination is also nested.

Let's just go back to a smaller subset of results. I'll call it FirstThreeProducts. I'm going to get rid of the pagination information at the top level of products. So, I'm going to get rid of cursor and pageInfo and let's say, on products, I was also interested in their variants and their title.

graphql pagination: gif taken from youtube video

So variants return a list, a connection type, which is a list of edges

For whatever reason, let's naively say that I want the first two variants, but there's actually more than that. Let’s say I also wanted the edges, node, ID of each variant, and the title of each Id.

Now I can use those same pagination fields at the level of variance.

So next to edges there was pageInfo, and on the edge itself was a property called cursor. We also want to expand the information on pageInfo for hasNext and hasPrevious.

Now I've got some nested pagination going on here, so I can do pagination at every level of lists that I have. 

graphql pagination: gif taken from youtube video

What this is saying is that I've requested the first two variants, but there is actually another page of variants for this product. For this next project down here, Classic Varsity Top, that also has a nextPage and so on and so forth.

I could use exactly the same principles per the schema to do things like after this particular cursor.

Stay tuned for the final part of this tutorial where we'll be looking at a special argument that you can supply to any field that returns connections: the query argument. If you missed it, you can start at the beginning of this series with GraphQL operation names and variables.

Grow your business with the Shopify Partner Program

Learn more