Created
November 5, 2019 11:54
-
-
Save toyflish/ca9157b6eeeac7543885287486ed88e4 to your computer and use it in GitHub Desktop.
craftcms preview with nextjs graphql apollo-client
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 React from 'react' | |
import Head from 'next/head' | |
import { ApolloProvider } from '@apollo/react-hooks' | |
import { ApolloClient } from 'apollo-client' | |
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory' | |
import { createHttpLink } from 'apollo-link-http'; | |
import { setContext } from 'apollo-link-context'; | |
import fetch from 'isomorphic-unfetch' | |
import introspectionQueryResultData from '../fragmentTypes.json' | |
import querystring from 'querystring' | |
let apolloClient = null | |
const fragmentMatcher = new IntrospectionFragmentMatcher({ | |
introspectionQueryResultData | |
}); | |
/** | |
* Creates and provides the apolloContext | |
* to a next.js PageTree. Use it by wrapping | |
* your PageComponent via HOC pattern. | |
* @param {Function|Class} PageComponent | |
* @param {Object} [config] | |
* @param {Boolean} [config.ssr=true] | |
*/ | |
export function withApollo(PageComponent, { ssr = true } = {}) { | |
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => { | |
const token = pageProps.router.query.token | |
const client = apolloClient || initApolloClient({ ...apolloState, token }) | |
return ( | |
<ApolloProvider client={client}> | |
<PageComponent {...pageProps} /> | |
</ApolloProvider> | |
) | |
} | |
// Set the correct displayName in development | |
if (process.env.NODE_ENV !== 'production') { | |
const displayName = | |
PageComponent.displayName || PageComponent.name || 'Component' | |
if (displayName === 'App') { | |
console.warn('This withApollo HOC only works with PageComponents.') | |
} | |
WithApollo.displayName = `withApollo(${displayName})` | |
} | |
if (ssr || PageComponent.getInitialProps) { | |
WithApollo.getInitialProps = async ctx => { | |
const { AppTree } = ctx | |
const token = querystring.parse(ctx.req.url).token | |
const apolloClient = (ctx.apolloClient = initApolloClient({ token })) | |
// Run wrapped getInitialProps methods | |
let pageProps = {} | |
if (PageComponent.getInitialProps) { | |
pageProps = await PageComponent.getInitialProps(ctx) | |
} | |
// Only on the server: | |
if (typeof window === 'undefined') { | |
// When redirecting, the response is finished. | |
// No point in continuing to render | |
if (ctx.res && ctx.res.finished) { | |
return pageProps | |
} | |
// Only if ssr is enabled | |
if (ssr) { | |
try { | |
// Run all GraphQL queries | |
const { getDataFromTree } = await import('@apollo/react-ssr') | |
await getDataFromTree( | |
<AppTree | |
pageProps={{ | |
...pageProps, | |
apolloClient | |
}} | |
/> | |
) | |
} catch (error) { | |
// Prevent Apollo Client GraphQL errors from crashing SSR. | |
// Handle them in components via the data.error prop: | |
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error | |
console.error('Error while running `getDataFromTree`', error) | |
} | |
// getDataFromTree does not call componentWillUnmount | |
// head side effect therefore need to be cleared manually | |
Head.rewind() | |
} | |
} | |
// Extract query data from the Apollo store | |
const apolloState = apolloClient.cache.extract() | |
return { | |
...pageProps, | |
apolloState | |
} | |
} | |
} | |
return WithApollo | |
} | |
/** | |
* Always creates a new apollo client on the server | |
* Creates or reuses apollo client in the browser. | |
* @param {Object} initialState | |
*/ | |
function initApolloClient(initialState) { | |
// Make sure to create a new client for every server-side request so that data | |
// isn't shared between connections (which would be bad) | |
if (typeof window === 'undefined') { | |
return createApolloClient(initialState) | |
} | |
// Reuse client on the client-side | |
if (!apolloClient) { | |
apolloClient = createApolloClient(initialState) | |
} | |
return apolloClient | |
} | |
/** | |
* Creates and configures the ApolloClient | |
* @param {Object} [initialState={}] | |
*/ | |
function createApolloClient(initialState = {}) { | |
const token = initialState.token | |
const uri = `${process.env.CRAFT_GQL_URL}${token !== undefined ? `?token=${token}` : ''}` | |
const httpLink = createHttpLink({ | |
uri, | |
fetch | |
}); | |
const authLink = setContext((request, { headers }) => { | |
return { | |
headers: { | |
...headers | |
// authorization: process.env.CRAFT_GQL_TOKEN | |
// authorization: token ? `Bearer ${token}` : '' | |
} | |
} | |
}); | |
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient | |
return new ApolloClient({ | |
ssrMode: typeof window === 'undefined', // Disables forceFetch on the server (so queries are only run once) | |
link: authLink.concat(httpLink), | |
cache: new InMemoryCache({ fragmentMatcher }).restore(initialState) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
piping craftcms preview-token through nextjs apollo-configuration to have live preview