all repos — caroster @ cdf94c315c26f10b3ffc7e10ac40892c951d57a5

[Octree] Group carpool to your event https://caroster.io

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