Skip to content

Instantly share code, notes, and snippets.

@paulbellamy
Created June 3, 2020 12:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulbellamy/0e38e1b0fa7d2c997f6d2cadc6180859 to your computer and use it in GitHub Desktop.
Save paulbellamy/0e38e1b0fa7d2c997f6d2cadc6180859 to your computer and use it in GitHub Desktop.
Next.js Apollo graphql page with server-side query
import { ApolloProvider, useQuery } from "@apollo/react-hooks";
import ApolloClient, { InMemoryCache } from "apollo-boost";
import { GraphQLError } from "graphql";
import { GetServerSideProps } from "next";
import { useRouter } from "next/router";
import wrap, { InitApolloClient, WithApolloProps } from "next-with-apollo";
// Add our query here. If we want this in a different file we could use
// graphql-codegen to parse that for us and generate documents and hooks.
import gql from "graphql-tag";
const FindUserQuery = gql`
query($id: String!) {
findUser(id: $id) {
id
firstName
lastName
}
}
`;
// Set up an apollo client. This takes optional headers, so we can pass through
// the request headers. This code would be off in some util library, so you could
// re-use it across pages.
const getApollo: InitApolloClient<any> = ({
initialState,
headers,
} = {}) => {
const uri = process.env.NEXT_PUBLIC_GRAPHQL_URL;
return new ApolloClient({
// initialState here lets us extract and restore the cache!
cache: new InMemoryCache({}).restore(initialState || {}),
headers,
uri,
});
};
// Define the properties our page takes. It takes an apollo client cache.
type PageProps = Partial<WithApolloProps<any>> & {
errors?: GraphQLError[];
};
export const getServerSideProps: GetServerSideProps<PageProps> = async ({
query,
req,
res,
}) => {
// Set up our graphql client, this passes through the http request headers as part of the request.
const apolloClient = getApollo({ headers: req.headers });
// // Load the user data.
const { data, errors } = await apolloClient.query({
query: FindUserQuery,
variables: { id: query.id },
});
// If there was no user, return a 404 for this page. This is useful for SEO
// if we are doing dynamic routing.
if (!data?.findUser) {
res.statusCode = 404;
return { props: { errors: [new GraphQLError("404 Not Found")] } };
}
return {
props: {
apolloState: {
// This is the hack. We extract the cache from the apollo client and
// pass that through. This will serialize our data and send that through
// to the browser.
data: apolloClient.cache.extract(),
errors,
},
},
};
};
// Render our page.
function UserPage({ errors }: PageProps) {
const { query } = useRouter();
// This query will not actually hit the API, but will use the
// cached data from our PageProps
const { data } = useQuery(FindUserQuery, {
variables: { id: query.id },
});
if (errors) {
// render something for the errors here
return <div>{errors}</div>;
}
// render your page as normal.
return <div>{data.findUser.firstName}</div>;
}
// Use this to wrap the page and pass in Apollo client. Pretty standard
// from next-with-apollo, but note we use our getApollo function here,
// so that our passed initialState gets put into the cache.
const withApollo = wrap(getApollo, {
render: ({ Page, props }) => {
return (
<ApolloProvider client={props.apollo}>
<Page {...props} />
</ApolloProvider>
);
},
});
// Wrap and export our page.
export default withApollo(UserPage);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment