frontend/lib/apolloClient.ts (view raw)
1import {useMemo} from 'react';
2import {ApolloClient, HttpLink, InMemoryCache, from} 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
11// https://github.com/vercel/next.js/tree/canary/examples/with-apollo
12// https://github.com/vercel/next.js/tree/canary/examples/layout-component
13// https://www.apollographql.com/docs/react/networking/authentication/
14// https://www.apollographql.com/docs/react/data/error-handling/
15
16export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__';
17let apolloClient;
18
19const authLink = setContext((_, {headers}) => {
20 // get the authentication token from local storage if it exists
21 const {token} = useAuthStore.getState();
22 // return the headers to the context so httpLink can read them
23 return {
24 headers: {
25 ...headers,
26 authorization: token ? `Bearer ${token}` : '',
27 },
28 };
29});
30
31const errorLink = onError(({graphQLErrors = []}) => {
32 const [error] = graphQLErrors;
33 console.error({graphQLErrors});
34
35 if (
36 error?.message === 'Invalid token.' ||
37 error?.message === 'User Not Found' ||
38 error?.message === 'Your account has been blocked by the administrator.'
39 ) {
40 useAuthStore.getState().setToken();
41 useAuthStore.getState().setUser();
42 localStorage.removeItem('token');
43 localStorage.removeItem('user');
44 }
45});
46
47const httpLink = uri =>
48 new HttpLink({
49 uri,
50 credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
51 });
52
53const createApolloClient = () => {
54 return new ApolloClient({
55 ssrMode: typeof window === 'undefined',
56 link: from([authLink, errorLink, httpLink(`${STRAPI_URL}/graphql`)]),
57 cache: new InMemoryCache({
58 typePolicies: {
59 Event: {
60 fields: {
61 waitingList: {
62 merge(existing, incoming) {
63 return incoming;
64 },
65 },
66 },
67 },
68 },
69 }),
70 });
71};
72
73export const initializeApollo = (initialState = null) => {
74 const _apolloClient = apolloClient ?? createApolloClient();
75
76 // If your page has Next.js data fetching methods that use Apollo Client, the initial state gets hydrated here
77 if (initialState) {
78 // Get existing cache, loaded during client side data fetching
79 const existingCache = _apolloClient.extract();
80 // Merge the existing cache into data passed from getStaticProps/getServerSideProps
81 const data = merge(initialState, existingCache, {
82 // Combine arrays using object equality (like in sets)
83 arrayMerge: (destinationArray, sourceArray) => [
84 ...sourceArray,
85 ...destinationArray.filter(d => sourceArray.every(s => !isEqual(d, s))),
86 ],
87 });
88 // Restore the cache with the merged data
89 _apolloClient.cache.restore(data);
90 }
91
92 // For SSG and SSR always create a new Apollo Client
93 if (typeof window === 'undefined') return _apolloClient;
94
95 // Create the Apollo Client once in the client
96 if (!apolloClient) apolloClient = _apolloClient;
97 return _apolloClient;
98};
99
100export const addApolloState = (client, pageProps) => {
101 if (pageProps?.props) {
102 pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
103 }
104 return pageProps;
105};
106
107export const useApollo = pageProps => {
108 const state = pageProps[APOLLO_STATE_PROP_NAME];
109
110 return useMemo(() => initializeApollo(state), [state]);
111};