5 min read

How to set up Apollo with Next.js

This guide will walk you through setting up Apollo GraphQL with Next.js in TypeScript.
How to set up Apollo with Next.js
Photo by SpaceX / Unsplash

This guide will walk you through setting up Apollo GraphQL with Next.js in TypeScript. I highly recommend TypeScript with GraphQL to give you perfect types throughout your application. It will also generate the types for the server and client to have strong types throughout your application.

Let's get started!

Setup Next.js

The first step is to create your Next.js project. If you already have one, you can skip this step.

yarn create next-app --example with-typescript apollo-next-example

You can cd into your new project and explore. Next, we need to set up the Apollo server.

Apollo Server

Apollo server would be less heavy of a setup in an ideal world. Unfortunately, Apollo's server doesn't play nicely as a simple API endpoint. You'll need to install a few dependencies to get their server running.

yarn add -SE graphql apollo-server-micro micro-cors micro

Once you've installed the dependencies, you can start setting up the schema and resolvers.

Define the Schema

The GraphQL schema defines the queries. You often query for a specific resource (for example, find a user from an id), or a collection of resources (all users). The schema also defines what fields are available on each resource. You can easily hide certain information, such as passwords, that might exist on your model but should not be exposed to your users.

I like putting these files in lib/graphql. If that directory doesn't exist, create it. Then inside, add these two files:

lib/graphql/schema.ts

import { gql } from 'apollo-server-micro'

export const typeDefs = gql`
  type Query {
    example(id: Int!): Example!
  }
  
  type Example {
    name: String!
  }
`
lib/graphql/schema.ts

Above is your GraphQL Schema. You can define it within the code above or use a separate file. Defining in code works well with GraphQL plugins because you get all the syntax highlighting but don't need an extra build step.

The above schema is a small example to get you started. It defines a single query example that requires an integer id. That query will always respond with an object that is type Example. Below the query, it defines the structure of the object.

After defining the schema, you must write the code to execute the query. These are called resolvers. A query resolver should never change data; it looks it up and returns it. A mutation resolver can change data.

lib/graphql/resolvers.ts

const example = (parent: never, { id }: { id: number }, ctx: any) => {
  if (id === 1) {
    return {
      name: 'It worked!'
    }
  }
  return {
    name: 'No name'
  }
}

export const resolvers = {
  Query: {
    example
  }
}
lib/graphql/resolvers.ts

The naming here is essential. When you export the resolvers, it expects the names to match the schema. So Query is exported to match Query in the schema above. And example is the same name as the query you wrote. This is how Apollo will match the schema to the resolver.

Next, create the Next.js API route that will serve the GraphQL endpoint. Create a file at pages/api/query.ts.

In that file, put the following:

import { ApolloServer } from 'apollo-server-micro'
import Cors from 'micro-cors'
import { resolvers } from '../../lib/graphql/resolvers'
import { typeDefs } from '../../lib/graphql/schema'

const cors = Cors()
const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
})
const startServer = apolloServer.start()

export default cors(async function handler(req, res) {
  if (req.method === 'OPTIONS') {
    res.end()
    return false
  }
  await startServer

  await apolloServer.createHandler({
    path: '/api/query',
  })(req, res)
})

export const config = {
  api: {
    bodyParser: false,
  },
}
pages/api/query.ts

Above will create the Apollo server with the schema and the resolvers. I wish there were a lighter implementation than this, but this is how it currently works. We then start the server and serve the API request.

Setup the Apollo client

Once you have set up the server, you can query it with any GraphQL client. I like to keep things simple and use the same library as the server, but any GraphQL client will do. For testing queries, I'll use Insomnia to speed up iterations.

First, install the client:

yarn add -SE @apollo/client

Then, you will need to instantiate the client to use it across the app. Create a new file lib/apollo.ts and add:

import { ApolloClient, InMemoryCache } from '@apollo/client'

const uri = `${
  process.env.NEXT_PUBLIC_VERCEL_URL || process.env.NEXT_PUBLIC_API_ROOT
}/api/query`

const apolloClient = new ApolloClient({
  uri,
  cache: new InMemoryCache(),
  credentials: 'include'
})

export default apolloClient

The Apollo client needs the URL for what it will be querying. Because Next.js can make requests client or server-side, I like to pass in the full URL. To make this easily changeable per client, I put the following in an .env file:

NEXT_PUBLIC_API_ROOT=http://localhost:3000
.env

Once the client is instantiated, wrap the _app.tsx file with the Apollo provider:

import { ApolloProvider } from '@apollo/client'
import type { AppProps } from 'next/app'
import apolloClient from '../lib/apollo'

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ApolloProvider client={apolloClient}>
      <Component {...pageProps} />
    </ApolloProvider>
  )
}

export default MyApp
_app.tsx

This wraps your application in the Apollo provider so every page and component can use the client with the hook.

Using the client

To use the client, create a graphQL query and use the provided hook.

import { gql, useQuery } from '@apollo/client'

// Add at the top level
const exampleQuery = gql`
  query AnExample($id: Int!) {
    example(id: $id) {
      name
    }
  }
`

// And then in the page/component

const MyComponent = () => {
  const { data } = useQuery<AnExampleQuery>(exampleQuery, {
    variables: {
      id: 1
    }
  })
  
  return (
    <div>
      {data?.example.name}
    </div>
  )
}

The Apollo client allows you to pass in the types of the data and variables. Types are half of what makes GraphQL so powerful. So as the last step, add types.

Types

GraphQL is a strongly typed schema that pairs perfectly with TypeScript. As a result, you can create perfect types for each part of the stack.

First, install the required dependencies:

yarn add -SED @graphql-codegen/typed-document-node @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/cli

GraphQL Code Gen

Next, you need to configure the GraphQL Codegen library. Then Codegen knows where to find the schema and the queries.

Create the file codegen.yaml:

schema: http://localhost:3000/api/query
documents: '**/*.tsx'
generates:
  ./lib/graphql/generated.ts:
    plugins:
      - typescript
      - typescript-operations
      - typed-document-node
codegen.yaml

You can change some of the values if you want. It defines where to look up the live schema (not the schema file!) and where to place the file of generated types. Codegen uses the live running server! When running it locally, you need to have the dev server running. You can also point it to production if you want.

To run, do the following:

yarn run graphql-codegen

This will generate the types and create or update the file. You can now import them throughout the application.

Client Example

import {
  AnExampleQuery,
  AnExampleQueryVariables
} from '../lib/graphql/generated'

// ...

const { data } = useQuery<AnExampleQuery, AnExampleQueryVariables>(exampleQuery, {
  variables: {
    id: 1
   }
})

Server Example

import { QueryExampleArgs } from './generated'

const example = (parent: never, { id }: QueryExampleArgs, ctx: any) => {
  // same code
}

export const resolvers = {
  Query: {
    example
  }
}

And now, you can use GraphQL in your Next.js app!


I'm building a suite of internet products to find a better alternative to the next tech job. The first product is a SaaS app that helps manage localizations and copy for your app.

If you want to follow along, please find me on Twitter. It means a lot!