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 {signOut, useSession} from 'next-auth/react';
8import {Session} from 'next-auth';
9
10export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__';
11let apolloClient: ApolloClient<any>;
12
13const authLink = (session: Session | null) =>
14 setContext(async (_, {headers}) => {
15 const token = session?.token?.jwt;
16 // return the headers to the context so httpLink can read them
17 return {
18 headers: {
19 ...headers,
20 authorization: token ? `Bearer ${token}` : '',
21 },
22 };
23 });
24
25const errorLink = onError(({operation, networkError}) => {
26 console.error({networkError, operation});
27 const responseStatus = networkError?.response?.status;
28
29 if (responseStatus === 401)
30 signOut({
31 callbackUrl: '/auth/login',
32 });
33});
34
35const httpLink = (uri: string) =>
36 new HttpLink({
37 uri, // Server URL (must be absolute)
38 credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
39 });
40
41const createApolloClient = (uri: string, session: Session | null) => {
42 return new ApolloClient({
43 ssrMode: typeof window === 'undefined',
44 link: from([authLink(session), errorLink, httpLink(uri)]),
45 cache: new InMemoryCache(),
46 });
47};
48
49export const initializeApollo = (
50 uri: string,
51 session: Session | null,
52 initialState = null
53) => {
54 const _apolloClient = apolloClient ?? createApolloClient(uri, session);
55
56 // If your page has Next.js data fetching methods that use Apollo Client, the initial state gets hydrated here
57 if (initialState) {
58 // Get existing cache, loaded during client side data fetching
59 const existingCache = _apolloClient.extract();
60 // Merge the existing cache into data passed from getStaticProps/getServerSideProps
61 const data = merge(initialState, existingCache, {
62 // Combine arrays using object equality (like in sets)
63 arrayMerge: (destinationArray, sourceArray) => [
64 ...sourceArray,
65 ...destinationArray.filter(d => sourceArray.every(s => !isEqual(d, s))),
66 ],
67 });
68 // Restore the cache with the merged data
69 _apolloClient.cache.restore(data);
70 }
71
72 // For SSG and SSR always create a new Apollo Client
73 if (typeof window === 'undefined') return _apolloClient;
74
75 // Create the Apollo Client once in the client
76 if (!apolloClient) apolloClient = _apolloClient;
77 return _apolloClient;
78};
79
80export const addApolloState = (client: ApolloClient<any>, pageProps: any) => {
81 if (pageProps?.props) {
82 pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
83 }
84 return pageProps;
85};
86
87export const useApollo = (pageProps: any) => {
88 const state = pageProps[APOLLO_STATE_PROP_NAME];
89 const {data: session} = useSession();
90 return useMemo(() => initializeApollo('', session, state), [state, session]);
91};