Created
June 3, 2020 12:07
-
-
Save paulbellamy/0e38e1b0fa7d2c997f6d2cadc6180859 to your computer and use it in GitHub Desktop.
Next.js Apollo graphql page with server-side query
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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