Working with Apollo Client and Relay style pagination

Working with Apollo Client and Relay style pagination

Grafbase exposes a Relay-style Pagination API that you can use outside of Relay with other client libraries, such as Apollo Client.

Apollo Client is a JavaScript library that helps developers manage data in a client-side application. It is designed to work with GraphQL, and provides an easy-to-use DX for fetching and caching data.

Pagination is used to split large sets of data into smaller, more manageable subsets often referred to as "pages".

Using pagination can lead to greater performance for the end user as pages and databases have less data to load. Pagination also has SEO benefits as faster load times lead to greater user experience which most search engines rank higher over those that don't.

Offset-based pagination requires you to provide an offset (or page number) and a limit (or page size) to instruct the database which subset of data should be fetched. This approach is often slower for large datasets but it's often the easiest to implement.

Cursor-based pagination however is the better and safer option. This approach requires you to provide a "cursor" to indicate the position in the dataset that results should be retrieved. Each page of data includes a cursor that points to the next set of results which allowing the database to work more efficiently, and scales as your data grows.

Thankfully Grafbase follows the Relay specification for pagination and tools like Apollo Client make this easy with a utility that can attach to any query to correctly handle the caching of cursor-based paginated results.

Let's begin by creating a GraphQL backend using the Grafbase CLI.

In the root of your existing React + Apollo application run the following:

npx grafbase init --template https://github.com/grafbase/grafbase/tree/main/examples/react-apollo-relay-pagination

Then open the generated grafbase/schema.graphql file. You should see the following schema that defines your backend:

type Post @model { title: String! }

We can use the Grafbase CLI to run the GraphQL backend locally:

npx grafbase dev

You should see a success message that your GraphQL Playground and API is running at http://localhost:4000.

In this guide we'll only explore querying data from our backend, so you will need to populate the backend using a GraphQL mutation inside the playground.

mutation { postCreate(input: { title: "Hello Grafbase!" }) { post { id } } }

You'll want to make sure you install @apollo/client and graphql inside of your project if you are not already using Apollo Client:

npm install @apollo/client graphql

Now you will need to create a client:

import { ApolloClient, InMemoryCache } from '@apollo/client' const client = new ApolloClient({ uri: '<YOUR_GRAFBASE_API_URL>', cache: new InMemoryCache(), })

Now all that's left to do is wrap your application with ApolloProvider:

<ApolloProvider client={client}> <App /> </ApolloProvider>

Note: YOUR_GRAFBASE_API_URL when using the CLI will be http://localhost:4000/graphql

Where you have configured the ApolloClient instance is where you will need to update the typePolicies for the collection query.

In this example we use the postCollection and update the InMemoryChace to use relayStylePagination() utility:

import { relayStylePagination } from '@apollo/client/utilities' const client = new ApolloClient({ uri: '<YOUR_GRAFBASE_API_URL>', cache: new InMemoryCache({ typePolicies: { Query: { fields: { postCollection: relayStylePagination(), }, }, }, }), })

We will now use GraphQL variables for the cursor and pass it into the collection query arguments:

const POSTS_QUERY = gql` query GetPostsAfterCursor($cursor: String) { postCollection(first: 1, after: $cursor) { edges { node { id title } } pageInfo { endCursor hasNextPage } } } `

Notice above we are also fetching the pageInfo which includes the endCursor value we need to pass to our query, and the hasNextPage boolean. We can use this boolean to toggle displaying a load more button.

Now we have the query above all that's left to do is update our application to fetchMore when we click a button.

Where you invoke useQuery you will want to destructure fetchMore:

const { data, loading, fetchMore } = useQuery(POSTS_QUERY)

Now when you want to "fetch more" all you need to do is invoke that function and password the "end cursor" value. Here's a full look at the <App /> component that renders a list of posts with a <button> to "load more":

export default function App() { const { data, loading, fetchMore } = useQuery(POSTS_QUERY) if (loading) return <p>Loading posts...</p> return ( <div> <ul> {data.postCollection?.edges?.nodes.map(({ node: { id, title } }) => ( <li key={id}>{title}</li> ))} </ul> {data.postCollection?.pageInfo?.hasNextPage && ( <button onClick={() => fetchMore({ variables: { cursor: pageInfo.endCursor, }, }) } > Load more </button> )} </div> ) }

Here are some other guides you might find interesting when working with Grafbase and Apollo Client:

Get Started

Build a GraphQL backend for your Apollo Client app with Grafbase.