DEV Community

Cover image for Apollo pagination tutorial: using fetchMore()
Emma Goto ๐Ÿ™
Emma Goto ๐Ÿ™

Posted on โ€ข Originally published at emgoto.com on

16 1

Apollo pagination tutorial: using fetchMore()

When you need to fetch large amounts of data from your GraphQL server with Apollo, you may need to make use of its pagination feature. Pagination allows you fetch multiple "pages" of data (e.g. 100 at a time).

This tutorial will walk you through how to make use of Apollo's pagination in your React app.

Take a look at your GraphQL schema

The approach you take with pagination will depend entirely on how things have been structured in your GraphQL schema. In this post, weโ€™ll be assuming that the schema looks something like this:

type PageInfo {
   hasNextPage: Boolean!
}

type Connection {
    edges: [Edge]
    nodes: [Node]
    pageInfo: PageInfo!
}

type Edge {
    cursor: String!
    node: Node
}
Enter fullscreen mode Exit fullscreen mode

What's important to us here are the hasNextPage and cursor (the item index) values.

Using Apollo's fetchMore function

Along with the data you fetch from Apollo's useQuery hook, you can also grab a variable called fetchMore:

const { data, loading, error, refetch, fetchMore } =
    useQuery(QUERY,
        {
            variables,
            fetchPolicy,
            errorPolicy: 'all',
        },
    );
Enter fullscreen mode Exit fullscreen mode

The fetchMore function can be repeatedly called to get all your pages of data. But first, we'll be creating a couple of utility functions to make life easier.

Check that there is a next page

Your data will contain a hasNextPage value, so we'll need to check if this is set to true:

const getHasNextPage = (data) =>
    data.pageInfo.hasNextPage;
Enter fullscreen mode Exit fullscreen mode

Please note that your function may be slightly different depending on the structure of your schema!

Calculate the after value

When using fetchMore, we need to tell it what index to start fetching from. We do this by passing in an after value.

If we pass in 99 as our after value, weโ€™re saying to fetch the next batch after 99 (i.e. 100 onwards).

To calculate our after value, we'll need to find the cursor value of the last item in the list:

const getAfter = (data) =>
    data.edges && data.edges.length > 0
        ? data.edges[data.edges.length - 1].cursor
        : null;
Enter fullscreen mode Exit fullscreen mode

Create your updateQuery function

Finally, weโ€™re going to need an updateQuery function. After we fetch the next page worth of data, weโ€™ll need to merge that data in with our already fetched data.

const updateQuery = (previousResult, { fetchMoreResult }) => {
    if (!fetchMoreResult) {
        return previousResult;
    }

    const previousEdges = previousResult.edges;
    const fetchMoreEdges = fetchMoreResult.edges;

    fetchMoreResult.edges = [...previousEdges, ...fetchMoreEdges];

    return { ...fetchMoreResult }
}

Enter fullscreen mode Exit fullscreen mode

As with the other code examples, you may have to modify it to match your GraphQL schema.

There's a couple of key things to note when creating your updateQuery:

  • The shape of the data you are returning needs to exactly match what Apollo is expecting. If you try and modify or remove certain keys, your updateQuery wonโ€™t work
  • Donโ€™t modify the previousResult and return it! If you do, Apollo wonโ€™t recognise that anything has changed, and wonโ€™t re-render your app after youโ€™ve fetched more data. If we modify the fetchMoreResult, we can get around this problem.

Use fetchMore inside of a useEffect

Now that weโ€™ve got all our utility functions, we can bring them all together:

useEffect(() => {
    if (data && fetchMore) {
        const nextPage = getHasNextPage(data);
        const after = getAfter(data);

        if (nextPage && after !== null) {
            fetchMore({ updateQuery, variables: { after } });
        }
    }
}, [data, fetchMore, updateQuery]);
Enter fullscreen mode Exit fullscreen mode

Pass in the after value to your GraphQL query

Youโ€™ll notice that we are passing in after to our variables list in fetchMore. You will also need to make sure that your query is using this variable:

query Data ($after: String) {
    data (after: $after) {
        pageInfo {
            hasNextPage
        }
        edges {
            cursor
            node {
              // ... data from each node here
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

With fetchMore implemented, the data from your useQuery hook will now return multiple pages worth of data!

Conclusion

And thatโ€™s it! Hopefully this post may have cleared up some mysteries about how to use pagination with the Apollo Client.

If you havenโ€™t already, I also recommend checking out the Pagination page on the Apollo docs for additional information and use cases.

Image of Timescale

๐Ÿš€ pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applicationsโ€”without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post โ†’

Top comments (4)

Collapse
 
sudeeptodutta profile image
Sudeepto Dutta โ€ข

Great article @emma . I have recently updated from apollo-boost to @apollo/client v3.2.7.

Now the recommended way to handle pagination is to remove updateQuery and use merge and read function or use helper functions provided by the Apollo team for various different types of pagination (viz. offset-based, cursor-based)

I'm a front-end dev using a Relay Graphql backend. We have Relay style cursor based pagination. The documentation felt a bit sparse at the moment so currently finding it difficult to migrate my updateQuery implementations to field query as suggested by Apollo team in the form of console warnings.

Do you plan to update / release a new post to share how to handle pagination with the new changes ?

Thank You.

Collapse
 
emma profile image
Emma Goto ๐Ÿ™ โ€ข

Hi Sudeepto - unfortunately I don't think my team is looking to upgrade anytime soon so we're still using updateQuery for now!

Collapse
 
karanpratapsingh profile image
Karan Pratap Singh โ€ข

great article, relay pagination spec is great relay.dev/graphql/connections.htm

Collapse
 
emma profile image
Emma Goto ๐Ÿ™ โ€ข

Thank you!