GraphQL pagination

This tutorial assumes that you have successfully completed the Dashboard quick start tutorial, and that you still have the Fauna Dashboard open in a browser tab/window, on the GraphQL Playground screen.

If your Dashboard session has expired:

  1. Log in again.

  2. Select the graphql database.

  3. Click the GRAPHQL button in the left navigation.

Databases can contain a lot of data. Queries that attempt to return a large fraction of the data can strain the resources of the database server and client application.

This tutorial demonstrates how to use pagination, where only a small group of results is returned for any one query and subsequent queries can fetch the next or previous group.

Tutorial

  1. Create a new schema file

    Create the file schema-paginate.gql with the following content (or download it here):

    type Letter {
      letter: String!
    }
    
    type Query {
      allLetters: [Letter!]
    }
  2. Import the new GraphQL schema into Fauna

    Click the MERGE SCHEMA button in the GraphQL Playground screen (in your browser), which opens your browser’s file selector. Select the schema-paginate.gql file, and click the file selector’s Open button.

    This new schema only updates collections (and associated indexes) with the same name. Any other collections are unaffected.
  3. Create some records

    Let’s create some documents. Bulk creation of documents is easiest to do using FQL.

    Open a terminal and run:

    fauna shell graphql

    After Fauna Shell starts, run the following query:

    Map(
      ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"],
      Lambda("X", Create(Collection("Letter"), { data: { letter: Var("X") }}))
    )

    You should see output similar to:

    [ { ref: Ref(Collection("Letter"), "234438806828235266"),
        ts: 1559837118920000,
        data: { letter: 'a' } },
      { ref: Ref(Collection("Letter"), "234438806828228098"),
        ts: 1559837118920000,
        data: { letter: 'b' } },
      { ref: Ref(Collection("Letter"), "234438806828241410"),
        ts: 1559837118920000,
        data: { letter: 'c' } },
      { ref: Ref(Collection("Letter"), "234438806828231170"),
        ts: 1559837118920000,
        data: { letter: 'd' } },
      { ref: Ref(Collection("Letter"), "234438806828237314"),
        ts: 1559837118920000,
        data: { letter: 'e' } },
      { ref: Ref(Collection("Letter"), "234438806828224002"),
        ts: 1559837118920000,
        data: { letter: 'f' } },
      { ref: Ref(Collection("Letter"), "234438806828242434"),
        ts: 1559837118920000,
        data: { letter: 'g' } },
      { ref: Ref(Collection("Letter"), "234438806828238338"),
        ts: 1559837118920000,
        data: { letter: 'h' } },
      { ref: Ref(Collection("Letter"), "234438806828233218"),
        ts: 1559837118920000,
        data: { letter: 'i' } },
      { ref: Ref(Collection("Letter"), "234438806828230146"),
        ts: 1559837118920000,
        data: { letter: 'j' } },
      { ref: Ref(Collection("Letter"), "234438806828243458"),
        ts: 1559837118920000,
        data: { letter: 'k' } },
      { ref: Ref(Collection("Letter"), "234438806828244482"),
        ts: 1559837118920000,
        data: { letter: 'l' } },
      { ref: Ref(Collection("Letter"), "234438806828240386"),
        ts: 1559837118920000,
        data: { letter: 'm' } },
      { ref: Ref(Collection("Letter"), "234438806828227074"),
        ts: 1559837118920000,
        data: { letter: 'n' } },
      { ref: Ref(Collection("Letter"), "234438806828226050"),
        ts: 1559837118920000,
        data: { letter: 'o' } },
      { ref: Ref(Collection("Letter"), "234438806828234242"),
        ts: 1559837118920000,
        data: { letter: 'p' } },
      { ref: Ref(Collection("Letter"), "234438806828245506"),
        ts: 1559837118920000,
        data: { letter: 'q' } },
      { ref: Ref(Collection("Letter"), "234438806828220930"),
        ts: 1559837118920000,
        data: { letter: 'r' } },
      { ref: Ref(Collection("Letter"), "234438806828221954"),
        ts: 1559837118920000,
        data: { letter: 's' } },
      { ref: Ref(Collection("Letter"), "234438806828239362"),
        ts: 1559837118920000,
        data: { letter: 't' } },
      { ref: Ref(Collection("Letter"), "234438806828225026"),
        ts: 1559837118920000,
        data: { letter: 'u' } },
      { ref: Ref(Collection("Letter"), "234438806828232194"),
        ts: 1559837118920000,
        data: { letter: 'v' } },
      { ref: Ref(Collection("Letter"), "234438806828246530"),
        ts: 1559837118920000,
        data: { letter: 'w' } },
      { ref: Ref(Collection("Letter"), "234438806828236290"),
        ts: 1559837118920000,
        data: { letter: 'x' } },
      { ref: Ref(Collection("Letter"), "234438806828229122"),
        ts: 1559837118920000,
        data: { letter: 'y' } },
      { ref: Ref(Collection("Letter"), "234438806828222978"),
        ts: 1559837118920000,
        data: { letter: 'z' } } ]
  4. Query for all letters

    Let’s verify that GraphQL Playground can see all of the letters.

    Copy the following GraphQL query:

    query FindAllLetters {
      allLetters {
        data {
          _id
          letter
        }
      }
    }

    Then click the "new tab" + button on the GraphQL Playground screen in your browser (at the top left, just right of the last query tab). Paste the query into the left panel, and click the "Play" button. The query should execute and the response should appear in the right panel:

    {
      "data": {
        "allLetters": {
          "data": [
            {
              "_id": "234439110495830537",
              "letter": "a"
            },
            {
              "_id": "234439110495831561",
              "letter": "f"
            },
            {
              "_id": "234439110495832585",
              "letter": "g"
            },
            {
              "_id": "234439110495833609",
              "letter": "i"
            },
            {
              "_id": "234439110495834633",
              "letter": "c"
            },
            {
              "_id": "234439110495835657",
              "letter": "l"
            },
            {
              "_id": "234439110495836681",
              "letter": "h"
            },
            {
              "_id": "234439110495837705",
              "letter": "d"
            },
            {
              "_id": "234439110495838729",
              "letter": "b"
            },
            {
              "_id": "234439110495839753",
              "letter": "j"
            },
            {
              "_id": "234439110495840777",
              "letter": "w"
            },
            {
              "_id": "234439110495841801",
              "letter": "m"
            },
            {
              "_id": "234439110495842825",
              "letter": "n"
            },
            {
              "_id": "234439110495843849",
              "letter": "o"
            },
            {
              "_id": "234439110495844873",
              "letter": "p"
            },
            {
              "_id": "234439110495845897",
              "letter": "s"
            },
            {
              "_id": "234439110495846921",
              "letter": "k"
            },
            {
              "_id": "234439110495847945",
              "letter": "e"
            },
            {
              "_id": "234439110495848969",
              "letter": "r"
            },
            {
              "_id": "234439110495849993",
              "letter": "y"
            },
            {
              "_id": "234439110495851017",
              "letter": "q"
            },
            {
              "_id": "234439110495852041",
              "letter": "x"
            },
            {
              "_id": "234439110495853065",
              "letter": "u"
            },
            {
              "_id": "234439110495854089",
              "letter": "v"
            },
            {
              "_id": "234439110495855113",
              "letter": "z"
            },
            {
              "_id": "234439110495856137",
              "letter": "t"
            }
          ]
        }
      }
    }
  5. Query for a small group of letters

    How should we query for only a small group of letters? When we defined the allLetters query, the GraphQL API automatically created an index for the documents in the Letter collection. Any queries which involve lists of values automatically accept a _size parameter, specifying the maximum number of documents to return, and a _cursor parameter, specifying a marker that describes the position and direction within the result set.

    Modify the query to look like this:

    query FindAllLetters {
      allLetters(_size: 5) {
        data {
          _id
          letter
        }
      }
    }

    Then click the "Play" button. The query should execute and the response should appear in the right panel:

    {
      "data": {
        "allLetters": {
          "data": [
            {
              "_id": "234439110495830537",
              "letter": "a"
            },
            {
              "_id": "234439110495831561",
              "letter": "f"
            },
            {
              "_id": "234439110495832585",
              "letter": "g"
            },
            {
              "_id": "234439110495833609",
              "letter": "i"
            },
            {
              "_id": "234439110495834633",
              "letter": "c"
            }
          ]
        }
      }
    }

    By specifying _size: 5, we are telling GraphQL that we only want 5 records, and that’s how many we received. Had we specified 1,000 instead, we would only receive 26 documents, since that’s as many as the collection contains. If you don’t specify _size, you get at most 50 results.

  6. Query for groups of letters using cursors

    How would we get the next group of letters? First, we need to modify the query a little to retrieve the cursor information:

    query FindSomeLetters {
      allLetters(_size: 5) {
        data {
          _id
          letter
        }
        before
        after
      }
    }

    Then click the "Play" button. The query should execute and the response should appear in the right panel:

    {
      "data": {
        "allLetters": {
          "data": [
            {
              "_id": "234439110495830537",
              "letter": "a"
            },
            {
              "_id": "234439110495831561",
              "letter": "f"
            },
            {
              "_id": "234439110495832585",
              "letter": "g"
            },
            {
              "_id": "234439110495833609",
              "letter": "i"
            },
            {
              "_id": "234439110495834633",
              "letter": "c"
            }
          ],
          "before": null,
          "after": "2DOB2DRyMjM0NDM5MTEwNDk1ODM1NjU3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA=="
        }
      }
    }

    By asking for before and after in the query, GraphQL includes those values in the result. These represent cursors, which are markers that define a position in the results, as well as the direction involved.

    The before cursor is null, which tells us that there are no groups of documents before the current results. The after cursor tells us that there are more results after the c result. Let’s see those results. Modify the query to look like this:

    query FindSomeLetters {
      allLetters(_size: 5, _cursor: "2DOB2DRyMjM0NDM5MTEwNDk1ODM1NjU3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA==") {
        data {
          _id
          letter
        }
        before
        after
      }
    }

    Replace 2DOB2DRyMjM0NDM5MTEwNDk1ODM1NjU3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA== with the value of the after cursor from the previous query’s output. Then click the "Play" button. The query should execute and the response should appear in the right panel:

    {
      "data": {
        "allLetters": {
          "data": [
            {
              "_id": "234439110495835657",
              "letter": "l"
            },
            {
              "_id": "234439110495836681",
              "letter": "h"
            },
            {
              "_id": "234439110495837705",
              "letter": "d"
            },
            {
              "_id": "234439110495838729",
              "letter": "b"
            },
            {
              "_id": "234439110495839753",
              "letter": "j"
            }
          ],
          "before": "2DKB2DRyMjM0NDM5MTEwNDk1ODM1NjU3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA==",
          "after": "2DOB2DRyMjM0NDM5MTEwNDk1ODQwNzc3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA=="
        }
      }
    }

    Notice that we have a new group of letters, the before cursor now indicates that there are letters before this group, and because the after cursor is not null, there are more letters after this group.

    What happens if we change _size when we’re not at the start of the result set? Let’s try that! Modify the query so that _size is now 30:

    query FindSomeLetters {
      allLetters(_size: 30, _cursor: "2DOB2DRyMjM0NDM5MTEwNDk1ODM1NjU3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA==") {
        data {
          _id
          letter
        }
        before
        after
      }
    }

    Replace 2DOB2DRyMjM0NDM5MTEwNDk1ODM1NjU3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA== with the value of the after cursor from the previous query’s output. Then click the "Play" button. The query should execute and the response should appear in the right panel:

    {
      "data": {
        "allLetters": {
          "data": [
            {
              "_id": "234439110495835657",
              "letter": "l"
            },
            {
              "_id": "234439110495836681",
              "letter": "h"
            },
            {
              "_id": "234439110495837705",
              "letter": "d"
            },
            {
              "_id": "234439110495838729",
              "letter": "b"
            },
            {
              "_id": "234439110495839753",
              "letter": "j"
            },
            {
              "_id": "234439110495840777",
              "letter": "w"
            },
            {
              "_id": "234439110495841801",
              "letter": "m"
            },
            {
              "_id": "234439110495842825",
              "letter": "n"
            },
            {
              "_id": "234439110495843849",
              "letter": "o"
            },
            {
              "_id": "234439110495844873",
              "letter": "p"
            },
            {
              "_id": "234439110495845897",
              "letter": "s"
            },
            {
              "_id": "234439110495846921",
              "letter": "k"
            },
            {
              "_id": "234439110495847945",
              "letter": "e"
            },
            {
              "_id": "234439110495848969",
              "letter": "r"
            },
            {
              "_id": "234439110495849993",
              "letter": "y"
            },
            {
              "_id": "234439110495851017",
              "letter": "q"
            },
            {
              "_id": "234439110495852041",
              "letter": "x"
            },
            {
              "_id": "234439110495853065",
              "letter": "u"
            },
            {
              "_id": "234439110495854089",
              "letter": "v"
            },
            {
              "_id": "234439110495855113",
              "letter": "z"
            },
            {
              "_id": "234439110495856137",
              "letter": "t"
            }
          ],
          "before": "2DKB2DRyMjM0NDM5MTEwNDk1ODM1NjU3gWdMZXR0ZXJzgWdjbGFzc2VzgICAgA==",
          "after": null
        }
      }
    }

    Since we asked for 30 results, and we only had 26 altogether, and we started at the 6th result, we see all of the remaining letters in the results. The before cursor tells us that there are letters before this group. Since the after cursor is null, there are no letters after this group.

Conclusion

This tutorial has demonstrated how pagination works in GraphQL, shown you how to constrain the number of results, and how to issue subsequent queries to return different groups from a larger result set.

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!