March 9, 2021

Getting Started With Apollo Client in Next.js

Kurt Kemple

Kurt Kemple

(Update May 2023) We published a library that makes it easier to use Apollo Client with Next.js 13, this guide still remains valid if you’re using the pages directory.

Wait, you want me to put my data where!? Figuring out how to configure Apollo Client with Next.js can be a bit confusing. This confusion stems from the fact that there are three ways to fetch and render data: static, server-side, and client-side. There are benefits and tradeoffs to each strategy, but I’m happy to report that Apollo Client is compatible with all three!

In this post, we’ll briefly cover each way to fetch and render data with Next.js, along with the important considerations for each approach. We’ll wrap up with a demonstration of how to configure Apollo Client for each one.

If you’re unfamiliar with how pages are rendered in Next.js, check out these guides on pre-rendering and the two types of pre-rendering Next.js supports.

A quick comparison of data fetching options in Next.js

Static Rendering

With static rendering, your pages are generated at build time and then served as static HTML files. This method provides speedy response times because the pages don’t need to be rendered for each request. SEO also works as expected for statically rendered pages because the content is readily available for crawlers. However, statically rendered content can only be updated by deploying a new version of your app. This means that relative to other methods, your content is more likely to become stale between deploys.

Server-side Rendering

With server-side rendering, pages are generated dynamically for each incoming request. This enables content to be updated between deploys (unlike static rendering), and SEO works as expected. However, rendering on every request does add processing time that can noticeably increase response latency.

Client-side Rendering

With client-side rendering (the typical strategy for React apps), the browser requests the app, the page loads, then React requests the data to present to the user. This strategy keeps pages fresh and responsive, but it isn’t compatible with SEO crawlers.

Creating a New Next.js App

Now that we understand our options let’s start setting up an app to try them out.

To create a new Next.js application, we’ll use create-next-app:

npx create-next-app next-with-apollo

You can change next-with-apollo to be any project name, have fun with it, or choose the name of that project you want to build. 🙂

With the app scaffolded and dependencies installed, we can now run the application:

cd next-with-apollo
npm run dev

Navigating to http://localhost:3000 will open the running Next.js app in your browser!

Setting Up Apollo Client

Now that the app is created, it’s time to add Apollo Client and connect it to an API. In this example, we’ll use the Countries API from Trevor Blades to display country codes and flags for each country.

The complete code can be found here.

npm install @apollo/client graphql

With our dependencies in place, it’s now time to set up the client in our application. Create a new file in the root of the project called apollo-client.js and add the following contents to it:

// ./apollo-client.js

import { ApolloClient, InMemoryCache } from "@apollo/client";

const createApolloClient = () => {
  return new ApolloClient({
    uri: "https://countries.trevorblades.com",
    cache: new InMemoryCache(),
  });
};

export default createApolloClient;

Now we have a GraphQL client we can use within the application, and we’re ready to query for some data. However, before we can query for anything, we first need to know what data is available to us from the Countries API.

Exploring the API

We’ll use the Apollo Explorer, a powerful GraphQL IDE, to work with the Countries API. The Explorer connects to a graph and gives you a workspace to build, run, and save queries.

If you don’t already have an account, you can create one using your GitHub account. When you’re in, you’ll want to click the “New Graph” button at the top right.

Next, you’ll be prompted to give your new graph a title and choose a graph type. You’ll also want to change the endpoint to https://countries.trevorblades.com.

Once the graph is created, Apollo Studio will drop you into the Apollo Explorer.

Replace the example query in your Operations window with the following query.

query ExampleQuery {
  countries {
    code
    name
  }
}

And if we run the example by clicking the blue play button, we should get a JSON result back like this:

"data": {
    "countries": [
      {
        "code": "AD",
        "name": "Andorra"
      },
      {
        "code": "AE",
        "name": "United Arab Emirates"
      },
      {
        "code": "AF",
        "name": "Afghanistan"
      },
      {
        "code": "AG",
        "name": "Antigua and Barbuda"
      },
      ...
   ]
}

BOOM! Now we’re ready to jump back into the application and start fetching data!

Using Apollo Client for statically rendered page data

Before we can fetch data from the API, we have to set up our page. We do this for statically generated pages using the getStaticProps method. Next.js will use this function during the build process to get any data needed to be passed into the page component as props.

Because we’re using the methods provided by Next.js to fetch the data, we’re not going to be calling our API from within React itself. So we’ll use Apollo Client directly instead of using hooks.

Inside of pages/index.js import the client we created in apollo-client.js and gql from @apollo/client.

// pages/index.js

import { gql } from "@apollo/client";
import createApolloClient from "../apollo-client";

Then add the following code below the React component:

// pages/index.js

export async function getStaticProps() {
  const client = createApolloClient();
  const { data } = await client.query({
    query: gql`
      query Countries {
        countries {
          code
          name
          emoji
        }
      }
    `,
  });

  return {
    props: {
      countries: data.countries.slice(0, 4),
    },
  };
}

This will fetch the code, name, and emoji for each country, and then we pass along the first four results to the React component via the countries prop. Now that we have the data available, it’s time to update the component to show the page’s data.

To use our countries data, we need to update the React component with the countries prop.

// pages/index.js

export default function Home({ countries }) {
...

With the countries available in the component, we can replace the page’s grid with the following code:

// pages/index.js

<div className={styles.grid}>
  {countries.map((country) => (
    <div key={country.code} className={styles.card}>
      <h3>{country.name}</h3>
      <p>
        {country.code} - {country.emoji}
      </p>
    </div>
  ))}
</div>

The page now includes the four countries we passed in from the getStaticProps method! Pretty awesome!

It’s important to note that Next.js will fetch the data at build time and, therefore, be stale.

Using Apollo Client for server side rendered page data

Fetching data for server-side generated pages is done using the getServerSideProps method provided by Next.js. This function will be used during each page request to get any data passed into the page component as props.

Fetching page data for server-side rendered pages isn’t very different from what we did for statically generated pages. In fact, it’s so similar we can use almost all of the work we just did.

Duplicate ./pages/index.js and rename it to server-side.js. Then change the name of the function that fetches the data from getStaticProps to getServerSideProps.

// pages/server-side.js

export async function getServerSideProps() {
  const client = createApolloClient();
  const { data } = await client.query({
    query: gql`
      query Countries {
        countries {
          code
          name
          emoji
        }
      }
    `,
  });

  return {
    props: {
      countries: data.countries.slice(0, 4),
    },
  };
}

Everything else stays the same! 🎉

The major difference here is that Next.js will fetch the country codes in this example for each page request instead of only at build time.

Using Apollo Client for client-side data

Client-side rendering is what we typically do in React apps. The browser requests the app, the page loads, then React requests the data and presents it to the user.

Setting up Apollo Client for use on the client side takes a few additional steps. Because we’ll be in React and want to use hooks, we’ll need to use ApolloProvider. Open up ./pages/_app.js and import ApolloProvider from @apollo/client, as well as the client we created earlier.

// pages/_app.js

import { ApolloProvider } from "@apollo/client";
import createApolloClient from "../apollo-client";

Now replace the component in _app.js with the following one:

// pages/_app.js

function MyApp({ Component, pageProps }) {
  const client = createApolloClient();
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

With the provider wrapping our root component, we can now use hooks anywhere within our app, but we aren’t quite done yet. If we start using hooks within our app now, we’ll end up making requests to our API during page rendering (either at build time or serve time, depending on how the page is configured). This isn’t ideal because the page will be generated before the requests can return, and Next.js can pass the data to the component.

Create a new directory called components in the project’s root and then a file inside that folder called ClientOnly.js.

Copy the following code into the new file:

// components/ClientOnly.js

import { useEffect, useState } from "react";

export default function ClientOnly({ children, ...delegated }) {
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() => {
    setHasMounted(true);
  }, []);

  if (!hasMounted) {
    return null;
  }

  return <div {...delegated}>{children}</div>;
}

To make sure we only request data from the browser, we have to ensure that the components using hooks are only rendered on the client. We can accomplish this by creating a component that only renders its children in the browser and not on the server.

For more on this pattern, check out this post by Josh Comeau.

Next, create another file in the components directory called Countries.js and add the following code:

// components/Countries.js

import { useQuery, gql } from "@apollo/client";
import styles from "../styles/Home.module.css";

const QUERY = gql`
  query Countries {
    countries {
      code
      name
      emoji
    }
  }
`;

export default function Countries() {
  const { data, loading, error } = useQuery(QUERY);

  if (loading) {
    return <h2>Loading...</h2>;
  }

  if (error) {
    console.error(error);
    return null;
  }

  const countries = data.countries.slice(0, 4);

  return (
    <div className={styles.grid}>
      {countries.map((country) => (
        <div key={country.code} className={styles.card}>
          <h3>{country.name}</h3>
          <p>
            {country.code} - {country.emoji}
          </p>
        </div>
      ))}
    </div>
  );
}

This component is responsible for fetching the countries and will be wrapped in the ClientOnly component we created previously.

Finally, create a new file in the pages directory called client-side.js and add the following code:

// pages/client-side.js

import Head from "next/head";
import styles from "../styles/Home.module.css";
import ClientOnly from "../components/ClientOnly";
import Countries from "../components/Countries";

export default function ClientSide() {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <ClientOnly>
          <Countries />
        </ClientOnly>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{" "}
          <img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
        </a>
      </footer>
    </div>
  );
}


This page is very similar to the other two pages, except that it has no getStaticProps or getServerSideProps methods because we’ll fetch the data once the page is rendered in the browser.

And that’s it! 👏

Now we have client-side data fetching in place!

Conclusion

Next.js provides data fetching methods in both statically rendered and server-side rendered pages through the getStaticProps and getServerSideProps methods. We used Apollo Client directly to query the countries API when working in client-side rendering mode.

On the client, we wrapped the root component in ApolloProvider. We used a custom component to ensure we only request the countries from the client to avoid sending duplicate requests during page rendering.

In a future post, we’ll look at how we can mix these data-fetching patterns to handle more advanced use cases.

Written by

Kurt Kemple

Kurt Kemple

Read more by Kurt Kemple