Created
November 13, 2019 15:17
-
-
Save blech75/74129f48b8d1827dd51b7161349ade76 to your computer and use it in GitHub Desktop.
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 { ApolloClient } from 'apollo-client'; | |
import { ApolloLink } from 'apollo-link'; | |
import ApolloLinkTimeout from 'apollo-link-timeout'; | |
import { HttpLink } from 'apollo-link-http'; | |
import { setContext } from 'apollo-link-context'; | |
import { createPersistedQueryLink } from 'apollo-link-persisted-queries'; | |
import { onError } from 'apollo-link-error'; | |
import { | |
InMemoryCache, | |
IntrospectionFragmentMatcher | |
} from 'apollo-cache-inmemory'; | |
import fetch from 'isomorphic-unfetch'; | |
import { getSurrogateKey } from 'util/cache'; | |
import introspectionQueryResultData from '../graphQLFragmentTypes.json'; | |
const fragmentMatcher = new IntrospectionFragmentMatcher({ | |
introspectionQueryResultData | |
}); | |
const isBrowser = typeof window !== 'undefined'; | |
let apolloClient = null; | |
// Polyfill fetch() on the server (used by apollo-client) | |
if (!isBrowser) { | |
global.fetch = fetch; | |
} | |
const LINK_TIMEOUT_MS = 20000; // 20 second timeout | |
const CACHED_QUERIES = [ | |
'page', | |
'fetchMoreContent', | |
// see pages/prism/* | |
'prismImage', | |
'prismColor', | |
'prismTag', | |
// gift guide 2019 | |
'filteredProductListings' | |
]; | |
function create(initialState, { headers = {}, onErrorHandler } = {}) { | |
const setReqContext = setContext(request => { | |
const { variables, operationName } = request; | |
// set no-cache header for uncached queries | |
if (!CACHED_QUERIES.includes(operationName)) { | |
return { | |
headers: { | |
'no-cache': 'true' | |
} | |
}; | |
} | |
// the goal here is to base the GQL query's cache key off the page's | |
// surrogate key, thereby binding the page and its data query together so we | |
// can easily purge both together. we also add a generic query key ('fegql') | |
// that allows us to purge over all GQL queries at once. | |
// | |
// NOTE: this assumes cacheable queries have a 'path' variable. since we | |
// want to cache prism queries, they have been modified to include a 'path' | |
// variable even though they don't technically need it. | |
// | |
// CLEANUP: devise a way to access the current page's path in this context | |
// irrespective of the 'path' query variable. or is this approach not so | |
// bad? | |
const pageKey = getSurrogateKey(variables.path || ''); | |
const cacheKey = `fegql ${pageKey}`.trim(); | |
// set headers for cached queries | |
return { | |
headers: { | |
// we don't want to cache queries for "preview" pages | |
...(variables.preview ? { 'no-cache': variables.preview } : undefined), | |
// Add key. Note, this is set on the REQUEST headers - Fastly will | |
// use it to set the correct RESPONSE header for the surrogate key | |
'cache-key': cacheKey | |
} | |
}; | |
}); | |
const errorLink = onError(e => { | |
if (onErrorHandler) { | |
onErrorHandler(e); | |
} | |
}); | |
const persistedLink = createPersistedQueryLink({ | |
// Use GETs client side | |
useGETForHashedQueries: isBrowser | |
}); | |
const timeoutLink = new ApolloLinkTimeout(LINK_TIMEOUT_MS); | |
const httpLink = new HttpLink({ | |
uri: process.env.GRAPHQL_URI, | |
credentials: 'same-origin', | |
headers | |
}); | |
const link = ApolloLink.from([ | |
setReqContext, | |
errorLink, | |
persistedLink, | |
timeoutLink, | |
httpLink | |
]); | |
return new ApolloClient({ | |
connectToDevTools: isBrowser, | |
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once) | |
link, | |
cache: new InMemoryCache({ | |
dataIdFromObject: object => { | |
if (object.__typename === null) { | |
return null; | |
} | |
return object.id ? `${object.__typename}_${object.id}` : null; | |
}, | |
fragmentMatcher | |
}).restore(initialState || {}) | |
}); | |
} | |
export default function initApollo(initialState, ops) { | |
// 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 (!isBrowser) { | |
return create(initialState, ops); | |
} | |
// Reuse client on the client-side | |
if (!apolloClient) { | |
apolloClient = create(initialState, ops); | |
} | |
return apolloClient; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment