<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kamil Milewski</title>
    <description>The latest articles on Forem by Kamil Milewski (@kamilmilewski).</description>
    <link>https://forem.com/kamilmilewski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F145567%2F78f68183-eb1f-4179-b1cb-b9ac2b7dc22f.jpeg</url>
      <title>Forem: Kamil Milewski</title>
      <link>https://forem.com/kamilmilewski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kamilmilewski"/>
    <language>en</language>
    <item>
      <title>GraphQL pagination in Rails</title>
      <dc:creator>Kamil Milewski</dc:creator>
      <pubDate>Wed, 19 Jun 2019 08:13:22 +0000</pubDate>
      <link>https://forem.com/2nit/graphql-pagination-in-rails-1575</link>
      <guid>https://forem.com/2nit/graphql-pagination-in-rails-1575</guid>
      <description>&lt;h2&gt;
  
  
  GraphQL cursor pagination in Rails.
&lt;/h2&gt;

&lt;p&gt;So you implemented some graphQL types but it just turns out that returning bazillions of records with each query introduces some performance issues. "It would be nice to have some pagination" you think to yourself. In this post we gonna build graphQL cursor-based pagination functionality. The intent of this post is to show as simple and straightforward as possible how to implement graphQL pagination in graphql-ruby. We'll also cover some nasty edge cases, so they won't ambush you in the wild ;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;I assume you are using the following ruby and gem versions:&lt;/p&gt;

&lt;p&gt;ruby 2.5 &lt;br&gt;
  rails 5.2&lt;br&gt;
  graphql-ruby 1.8.7&lt;/p&gt;

&lt;p&gt;First, let's create post model:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_j1fgQsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/szugw0bpfkd0gyvishbt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_j1fgQsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/szugw0bpfkd0gyvishbt.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
And seed some sample data to play with:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2W2wLtMs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rvrypywapbu8xlflnt02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2W2wLtMs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rvrypywapbu8xlflnt02.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
It's time for graphQL post type:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8IDTaPuD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mkngbr8fqp7v6dktponx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8IDTaPuD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mkngbr8fqp7v6dktponx.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
Let's attach it to graphQL schema:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lbDx7lH8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/n4vrz6lsqpv1dy9rcv72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lbDx7lH8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/n4vrz6lsqpv1dy9rcv72.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
For now, query for this resource looks like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hd9cFgFV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ip2tqc9lu6mu7duk9gwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hd9cFgFV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ip2tqc9lu6mu7duk9gwq.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
and graphQL is politely returning all hundred posts:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--otWqGkS7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2rx26k885x0g0u2tbouc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--otWqGkS7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2rx26k885x0g0u2tbouc.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But of course it's not quite what we want. So, let's implement basic cursor pagination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pagination
&lt;/h3&gt;

&lt;p&gt;For type with cursor pagination - aka connection, as it has been named by Facebook - we need to define... connection. To do so we are going to add another field to query type:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RmqpmssH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uml5gsmnbt3mejdghz7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmqpmssH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uml5gsmnbt3mejdghz7f.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
As you can see it's pretty straightforward. The interesting part here is invocation of connection_type which will create &lt;strong&gt;connection type&lt;/strong&gt; for &lt;strong&gt;Types::PostType&lt;/strong&gt;. Actually, we are done! Now the simplest query for this connection type goes like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nK5kQc-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dlcl3kxxf6ksrj9k3y9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nK5kQc-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dlcl3kxxf6ksrj9k3y9d.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
which returns records in following form:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UGxMDNkd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2p67xe2eohekeyv3hze2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UGxMDNkd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2p67xe2eohekeyv3hze2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Query and its result looks more complicated, that's for sure, but where is the actual pagination? For this, graphql-ruby exposes another field in all connection types called &lt;strong&gt;pageInfo&lt;/strong&gt;. There is also an additional &lt;strong&gt;cursor&lt;/strong&gt; field under &lt;strong&gt;node&lt;/strong&gt; which will contain a unique id for given node (it's not identical to post record id!). The final query takes following form:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6aKFaV9B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dim0c4zdky57wy3aebud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6aKFaV9B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dim0c4zdky57wy3aebud.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you probably noticed &lt;strong&gt;postsConnection&lt;/strong&gt; above takes additional argument: &lt;strong&gt;first&lt;/strong&gt; which is pretty self explanatory: "query for the first 3 edges (records)". Aforementioned &lt;strong&gt;pageInfo&lt;/strong&gt; field provides us with, as name suggests, pagination-related info. It's fields are self explanatory I believe. The final result:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cXWdcVlB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gah37lfv2v82g20868an.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cXWdcVlB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gah37lfv2v82g20868an.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
to get to the next page we'll provide postsConnection with additional argument:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--orbOgQc7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jxlq6wbpzc8nxqehowcv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--orbOgQc7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jxlq6wbpzc8nxqehowcv.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
...which translates to: "query for the first 3 records after node with cursor(node id): &lt;strong&gt;NA==&lt;/strong&gt;"&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotcha 1: &lt;strong&gt;hasPreviousPage&lt;/strong&gt; and &lt;strong&gt;hasNextPage&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You might presume that &lt;strong&gt;hasPreviousPage&lt;/strong&gt; and &lt;strong&gt;hasNextPage&lt;/strong&gt; will provide you with info suggested by its name. GraphQL has a little nasty suprise for you there: &lt;strong&gt;hasNextPage&lt;/strong&gt; has valid info only when you paginate forward, for example: &lt;strong&gt;postsConnection(first: 3)&lt;/strong&gt;. Analogous &lt;strong&gt;hasPreviousPage&lt;/strong&gt; when you go backward: &lt;strong&gt;postsConnection(last: 3)&lt;/strong&gt;. It will become more clear when you'll look at the example below:&lt;/p&gt;

&lt;p&gt;Let's query for first 5 cursor ids:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sGvtxkrz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3jeyfuwewamxj1bajvxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sGvtxkrz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3jeyfuwewamxj1bajvxo.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
First 5 cursors:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JvRfMTrT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u3xawpoldaptsvh9i9po.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JvRfMTrT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u3xawpoldaptsvh9i9po.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
Now let's query for 2 records after second one &lt;strong&gt;Mg==&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WiuHbFS5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rzdm9n2k1eshyagjitnx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WiuHbFS5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rzdm9n2k1eshyagjitnx.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
We get &lt;strong&gt;pageInfo&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pmyzOleB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qknzdqr42z3pdcuywlr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pmyzOleB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qknzdqr42z3pdcuywlr8.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite 2 records still being on the previous page, we still get &lt;strong&gt;"hasPreviousPage": false&lt;/strong&gt;. That's right: not &lt;strong&gt;null&lt;/strong&gt; or &lt;strong&gt;undefined&lt;/strong&gt; but obviously incorrect &lt;strong&gt;false&lt;/strong&gt;. And it's not a bug. It's this way by design.&lt;/p&gt;

&lt;p&gt;Explanation for this is that at Facebook, where GraphQL has its origins, infinite scroll is a way to go for pagination(news feed for example). And for this purpose, information about previous page when you scroll down is just not needed. It would only introduce unnecessary performance overhead. One simple way to mitigate this in the current state of affairs(not very elegant though...) is to make additional query, just to check if there is a previous page. So for the query above it would look like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RMU-alKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/h50ch7gjr7z49ri3ijdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RMU-alKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/h50ch7gjr7z49ri3ijdb.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
..which provides us with correct &lt;strong&gt;hasPreviousPage&lt;/strong&gt; info this time:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9DguWnIy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fum4urjaeyfagn97psu4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9DguWnIy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fum4urjaeyfagn97psu4.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotcha 2: &lt;strong&gt;pageCount&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;So, you would probably like to know how many pages there is actually. To calculate this, you could do the following: &lt;strong&gt;total_records_count / records_per_page_count&lt;/strong&gt;. So all you really need is info about total records count. It would be nice to query for it like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GNyuytAv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6pvkx2kvwdpxj1t0kqdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GNyuytAv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6pvkx2kvwdpxj1t0kqdc.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
Unfortunately, running this query will result in:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WRT2vWTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4ae8yk6lt45ttiih9bm5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WRT2vWTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4ae8yk6lt45ttiih9bm5.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, there is no way to get this info from default connection. The reason is the same as in the case of previous and next page info: total records count is not really needed for infinite scroll pagination and would only introduce performance overhead. The good news is that it can be quite easily implemented though :)&lt;/p&gt;

&lt;p&gt;First, we need to define manually our custom connection:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XrHcNcdK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/whrem6wodvhfe7chzaj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XrHcNcdK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/whrem6wodvhfe7chzaj5.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
So, to define our custom connection we define a class which inherits from a base graphql connection class: &lt;strong&gt;GraphQL::Types::Relay::BaseConnection&lt;/strong&gt;. We also have to define by hand an edge class &lt;strong&gt;PostsEdgeType&lt;/strong&gt; and then specify that we are going to use it in our connection: &lt;strong&gt;edge_type(PostsEdgeType)&lt;/strong&gt;. Finally way we can add additional fields, like total_count in our example, just the way you would do this in other type classes, like &lt;strong&gt;Types::PostType&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next, we need to specify that we are going to use &lt;strong&gt;Types::PostsConnection&lt;/strong&gt; as posts_connection type class:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eK6wO7Rz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1dy74yy51js7w0zkuzob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eK6wO7Rz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1dy74yy51js7w0zkuzob.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
Now, our query with total count works as expected:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zQK6XLB0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/040fxiifnimh2asmdh31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQK6XLB0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/040fxiifnimh2asmdh31.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o9sNbjXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/taatwme2d908hm9v93ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o9sNbjXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/taatwme2d908hm9v93ly.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The drawback of this approach, is of course, that with &lt;strong&gt;totalCount&lt;/strong&gt; we are introducing N+1 to our connection. Also, watch out for class name used for our custom connection. It has to end with &lt;strong&gt;Connection&lt;/strong&gt; (as in our case &lt;strong&gt;Types::PostsConnection&lt;/strong&gt;) to be treated by graphql-ruby as connection. If you need a different naming convention you can state explicitly that a connection type will be used in field definition:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oy4a44kb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5e6da8n3z3jmwh4mp70e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oy4a44kb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5e6da8n3z3jmwh4mp70e.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;GraphQL, along with its implementations (like graphql-ruby in our case) is still a very young technology and under rapid development. Thus, there is lack of conventions that would be widely accepted by community, as we are used to in the Rails world. This also applies to graphQL pagination and we should expect changes to its specification and implementation in the future. But until then, this post should prove to be useful on your graphQL path ;)&lt;/p&gt;

&lt;p&gt;Full application code can be found &lt;a href="https://github.com/KamilMilewski/graphql_examples_for_blog/tree/cursor_pagination"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>pagination</category>
      <category>webdev</category>
      <category>rails</category>
    </item>
  </channel>
</rss>
