Skip to content

Instantly share code, notes, and snippets.

@blech75
Created November 13, 2019 15:17
Show Gist options
  • Save blech75/74129f48b8d1827dd51b7161349ade76 to your computer and use it in GitHub Desktop.
Save blech75/74129f48b8d1827dd51b7161349ade76 to your computer and use it in GitHub Desktop.
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