frontend/lib/apolloClient.tsx (view raw)
1import {useMemo} from 'react';
2import {ApolloClient, HttpLink, InMemoryCache} from '@apollo/client';
3import {setContext} from '@apollo/client/link/context';
4import {onError} from '@apollo/client/link/error';
5import merge from 'deepmerge';
6import isEqual from 'lodash/isEqual';
7import useAuthStore from '../stores/useAuthStore';
8
9const {STRAPI_URL = ''} = process.env;
10
11export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__';
12
13export let apolloClient;
14
15const httpLink = new HttpLink({
16 uri: `${STRAPI_URL}/graphql`, // Server URL (must be absolute)
17 credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
18});
19
20const handleError = onError(({graphQLErrors = [], ...details}) => {
21 const [error] = graphQLErrors;
22 console.error({graphQLErrors, error, details});
23
24 if (error?.message === 'Invalid token.') {
25 useAuthStore.getState().logout();
26 } else if (error?.message == 'Forbidden') {
27 window.location.href = '/auth/login';
28 }
29});
30
31const createApolloClient = token => {
32 return new ApolloClient({
33 ssrMode: typeof window === 'undefined',
34 link: setContext((_, {headers}) => {
35 // return the headers to the context so httpLink can read them
36 return {
37 headers: {
38 ...headers,
39 authorization: token ? `Bearer ${token}` : '',
40 },
41 };
42 })
43 .concat(handleError)
44 .concat(httpLink),
45 cache: new InMemoryCache(),
46 });
47};
48
49export const initializeApollo = (initialState: object, token: string) => {
50 const _apolloClient = createApolloClient(token);
51
52 // If your page has Next.js data fetching methods that use Apollo Client, the initial state gets hydrated here
53 if (initialState) {
54 // Get existing cache, loaded during client side data fetching
55 const existingCache = _apolloClient.extract();
56
57 // Merge the existing cache into data passed from getStaticProps/getServerSideProps
58 const data = merge(initialState, existingCache, {
59 // combine arrays using object equality (like in sets)
60 arrayMerge: (destinationArray, sourceArray) => [
61 ...sourceArray,
62 ...destinationArray.filter(d => sourceArray.every(s => !isEqual(d, s))),
63 ],
64 });
65
66 // Restore the cache with the merged data
67 _apolloClient.cache.restore(data);
68 }
69 // For SSG and SSR always create a new Apollo Client
70 if (typeof window === 'undefined') return _apolloClient;
71 // Create the Apollo Client once in the client
72 if (!apolloClient) apolloClient = _apolloClient;
73
74 return _apolloClient;
75};
76
77export const addApolloState = (client, pageProps) => {
78 if (pageProps?.props) {
79 pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
80 }
81
82 return pageProps;
83};
84
85export const useApollo = pageProps => {
86 const state = pageProps[APOLLO_STATE_PROP_NAME];
87 const token = useAuthStore(s => s.token);
88 return useMemo(() => {
89 const newClient = initializeApollo(state, token);
90 apolloClient = newClient;
91 return newClient;
92 }, [state, token]);
93};