Skip to content

Instantly share code, notes, and snippets.

@toyflish
Created November 5, 2019 11:54
Show Gist options
  • Save toyflish/ca9157b6eeeac7543885287486ed88e4 to your computer and use it in GitHub Desktop.
Save toyflish/ca9157b6eeeac7543885287486ed88e4 to your computer and use it in GitHub Desktop.
craftcms preview with nextjs graphql apollo-client
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)
})
}
@toyflish
Copy link
Author

toyflish commented Nov 5, 2019

piping craftcms preview-token through nextjs apollo-configuration to have live preview

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment