all repos — caroster @ v0.5.1

[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
 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};