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