Simple integration of Apollo client in a Next.js application. Easy setup with a simple but powerful API that can adapt to any use case.
npm
npm i nextjs-apollo-client @apollo/client graphqlor yarn
yarn add nextjs-apollo-client @apollo/client graphqlThe example below is the bare minimum requirements to get started. For more info about the API and usage continue reading. For more advanced usage check out the example.
- Create a new instance of
NextApolloClient
// lib/apollo/index.ts
export const { getServerSideApolloProps, useApolloClient } = new NextApolloClient({
client: { uri: 'http://mygraph.com/graphql' }
});- Add the Apollo provider to
_app.tsxpassing in the client
// _app.tsx
import type { AppProps } from 'next/app'
import { ApolloProvider } from '@apollo/client';
import { useApolloClient } from '../lib/apollo';
const App = ({ Component, pageProps }: AppProps) => {
const client = useApolloClient(pageProps);
return (
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
);
};
export default App- Utilize the generated
getServerSideApolloPropsto start hydrating data for your pages
import type { NextPage } from 'next'
import { useQuery } from '@apollo/client';
import { getServerSideApolloProps } from '../lib/apollo';
import { USERS_QUERY } from '../gql';
const Home: NextPage = () => {
const { data } = useQuery(USERS_QUERY);
return (
<div>
...rest
</div>
)
}
export const getServerSideProps = getServerSideApolloProps({
hydrateQueries: (ctx) => [
{ query: USER_QUERY, variables: { id: ctx.query.id } }
],
});
export default Home;Note:
The nextjs-apollo-client package works really well with next-merge-props.
Especially if you have multiple abstractions for Next.js server side data fetching functions.
import type { NextPage } from 'next'
import { useQuery } from '@apollo/client';
import { mergeProps } from 'next-merge-props';
import { getServerSideAuthProps } from '../lib/auth';
import { getServerSideApolloProps } from '../lib/apollo';
import { USERS_QUERY } from '../gql';
const Home: NextPage = () => {
const { data } = useQuery(USERS_QUERY);
return (
<div>
...rest
</div>
)
}
export const getServerSideProps = mergeProps(
getServerSideAuthProps,
getServerSideApolloProps({
hydrateQueries: (ctx) => [
{ query: USER_QUERY, variables: { id: ctx.query.id } }
],
})
);
export default Home;Below is a table that represents the structure of that NextApolloClient options object.
| Property | Type | Required |
|---|---|---|
| client | ApolloClientConfig | yes |
| hydrationMap | QueryHydrationMap | no (recommended) |
export interface NextApolloClientOptions {
client: ApolloClientConfig;
hydrationMap?: QueryHydrationMap;
}client
The only required option when creating an instance of NextApolloClient. Accepts a partial Apollo client config. For a complete list of available via partial config visit
the link to the types above.
Partial Config:
export const { getServerSideApolloProps, useApolloClient } = new NextApolloClient({
client: { uri: 'http://mygraph.com/graphql' },
});You can also pass in a function that returns an instance of ApolloClient which allows complete control of the Apollo
client setup. The function also gives you access to the current client cache state and Context
object from Next.js.
Apollo client:
export const { getServerSideApolloProps, useApolloClient } = new NextApolloClient({
client: (initialState, headers) =>
new ApolloClient({
ssrMode: typeof window === 'undefined',
cache: new InMemoryCache().restore(initialState),
link: new HttpLink({
uri: 'http://mygraph.com/graphql',
headers,
}),
}),
});hydrationMap
You can optionally pass in a hydration map. Once you have generated a hydration you can simply reference any key from
your map in the getServerSideApolloProps function to hydrate that page with any query in the hydration map.
The nextjs-apollo-client package includes a util to that you will need to use to generate the map: generateHydrationMap.
To get autocompletion for your hydration map, you can provide the instance of NextApolloClient with the hydration map
generic.
- Generate the hydration map
The generateHydrationMap function accepts a key/value pair. The key you use will also be the string you reference to
hydrate queries. The value is a function that provides access to the Next.js Context
object and must return a QueryOptions object.
// hydration map
const hydrationMap = generateHydrationMap({
user: (ctx): QueryOptions<UserQueryVariables, UserQuery> => ({
query: USER_QUERY,
variables: { id: ctx.query.userId as string }
}),
users: (): QueryOptions<UsersQueryVariables, UsersQuery> => ({ query: USERS_QUERY }),
});- Pass the hydration map to the
NextApolloClient
Now that you've generated the map, pass it into your NextApolloClient instance and provide the hydration map
generic for autocompletion.
export const { getServerSideApolloProps, useApolloClient } = new NextApolloClient<
typeof hydrationMap
>({
hydrationMap,
client: { uri: 'http://mygraph.com/graphql' },
});- Utilize your hydration map from
getServerSideApolloProps
Now you're ready to go! Now you can reference any query in your map via a string via autocompletion.
export const getServerSideProps = getServerSideApolloProps({
hydrateQueries: ['users'],
});The NextApolloClient instance exposes a public method called getServerSideApolloProps. This is the function
that should be used to hydrate any Next.js page component with data.
Below is a table that describes the accepted arguments.
| Property | Type | Required |
|---|---|---|
| hydrateQueries | HydrateQueries | no (if not provided all queries will be made on the client) |
| onClientInitialized | ClientInitFn | no |
| onHydrationComplete | (results: HydrationResponse) => ServerSidePropsResult | no |
export interface GetServerSideApolloPropsOptions<
THydrationMap extends QueryHydrationMap,
THydrationMapKeys = any,
TProps = Record<string, any>
> {
hydrateQueries?: HydrateQueries<THydrationMapKeys>;
onClientInitialized?: (
ctx: GetServerSidePropsContext,
apolloClient: ApolloClient<NormalizedCacheObject>
) => ServerSidePropsResult<TProps>;
onHydrationComplete?: (
results: HydrationResponse<THydrationMap>
) => ServerSidePropsResult<TProps>;
}hydrateQueries
This argument is not required, but it is required if you want to actually hydrate data on the server. There are a couple
of different ways hydrateQueries can be used.
- Use with a hydration map
If you have generated and provided NextApolloClient with a hydration map, you can simply reference your queries via map keys.
export const getServerSideProps = getServerSideApolloProps({
hydrateQueries: ['users'],
});- Use with
QueryOptionsarray
If your already familiar with Apollo client and have ever used refetchQueries as a side effect of mutation, this should
feel familiar. Accepts a function that provides the Next.js Context
object and must return an array of QueryOptions
objects.
export const getServerSideProps = getServerSideApolloProps({
hydrateQueries: [
{ query: USERS_QUERY }
],
});
// or as a function
export const getServerSideProps = getServerSideApolloProps({
hydrateQueries: (ctx) => [
{ query: USER_QUERY, variables: { id: ctx.query.id } }
],
});onClientInitialized
The getServerSideApolloProps function also exposes a couple of callbacks that allow you to hook into different lifecycles
of the function. The onClientInitialized callback does not assist with hydration but can still hydrate data. In fact,
it's basically an escape hatch that supports a wide variety of use cases.
- Mutation operation run on the server
Not a common use case, but if you need to do it you should do it here. A return is not required, but you can provide the
same return value
expected in any Next.js getServerSideProps function.
export const getServerSideProps = getServerSideApolloProps<PageProps>({
hydrateQueries: ['users'],
onClientInitialized: async (ctx, apolloClient) => {
const result = await apolloClient.mutate({
mutation: MY_MUTATION,
variables: {
id: ctx.query.id
}
});
return { props: { result } }
},
})onHydrationComplete
The second and last callback option is onHydrationComplete. This is used in conjunction with hydrateQueries and should
be more commonly used than onClientInitialized. The callback is run after any queries from hydrateQueries are run and
returns either the result of any hydration operation or errors from each query. Results for hydrated operations are mapped
to there operation name. If you have provided the hydration map generic to your instance of NextApolloClient, you
will get proper auto-completion for any of your hydrated operations.
A return is again not required, but you can provide the
same return value
expected in any Next.js getServerSideProps function.
interface PageProps {
userId?: string;
}
export const getServerSideProps = getServerSideApolloProps<PageProps>({
hydrateQueries: ['user'],
onHydrationComplete: ({ user, errors }) => {
const currentUser = user?.data.user;
if (errors.length) {
return { notFound: true };
}
if (!currentUser) {
return {
redirect: {
destination: '/',
permanent: false,
}
}
}
return {
props: { userId: currentUser.id },
};
}
});The only other public method returned from a NextApolloClient instance is useApolloClient. This react hook has only
one use case and that's to provide the client to Apollo client's context provider. The only argument passed to the hook
are the pageProps associated with the Next.js _app component.
import type { AppProps } from 'next/app'
import { ApolloProvider } from '@apollo/client';
import { useApolloClient } from '../lib/apollo';
const App = ({ Component, pageProps }: AppProps) => {
const client = useApolloClient(pageProps);
return (
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
);
};
export default AppThis project follows the all-contributors specification. Contributions of any kind welcome!
MIT

