import { useMemo } from "react"; import { ApolloClient, ApolloLink, from, HttpLink, InMemoryCache, NormalizedCacheObject, } from "@apollo/client"; import { concatPagination } from "@apollo/client/utilities"; import { clone, equals, mergeDeepWith } from "ramda"; import { onError } from "@apollo/client/link/error"; import { buildClientSchema, IntrospectionQuery } from "graphql"; import { DateTimeResolver } from "graphql-scalars"; import introspectionResult from "./graphql.schema.json"; // schema 文件 import { withScalars } from "apollo-link-scalars"; import superjson from "superjson"; const schema = buildClientSchema( introspectionResult as unknown as IntrospectionQuery ); const typesMap = { DateTime: DateTimeResolver, }; export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__"; let apolloClient: ApolloClient; function createApolloClient() { const httpLink = new HttpLink({ uri: typeof window === "undefined" ? process.env.BACKEND_URI : "/api/graphql", // Server URL (must be absolute) credentials: "same-origin", // Additional fetch() options like `credentials` or `headers` }); const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.forEach(({ message, locations, path }) => console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ) ); if (networkError) console.log(`[Network error]: ${networkError}`); }); return new ApolloClient({ ssrMode: typeof window === "undefined", link: from([ errorLink, withScalars({ schema, typesMap }) as ApolloLink, httpLink, ]), cache: new InMemoryCache({ typePolicies: { Query: { fields: { allPosts: concatPagination(), }, }, }, }), }); } export function initializeApollo(initialState = null) { const _apolloClient = apolloClient ?? createApolloClient(); // If your page has Next.js data fetching methods that use Apollo Client, the initial state // gets hydrated here if (initialState) { // Get existing cache, loaded during client side data fetching const existingCache = _apolloClient.extract(); // Merge the existing cache into data passed from getStaticProps/getServerSideProps const data = mergeDeepWith( (a, b) => { if (Array.isArray(a) && Array.isArray(b)) { return [...a, ...b.filter((bi) => a.every((ai) => !equals(ai, bi)))]; } else { return b; } }, initialState, existingCache ); // Restore the cache with the merged data _apolloClient.cache.restore(data); } // For SSG and SSR always create a new Apollo Client if (typeof window === "undefined") return _apolloClient; // Create the Apollo Client once in the client if (!apolloClient) apolloClient = _apolloClient; return _apolloClient; } export function addApolloState(client, pageProps) { if (pageProps?.props) { pageProps.props[APOLLO_STATE_PROP_NAME] = clone(client.cache.extract()); } return pageProps; } export function useApollo(pageProps) { const state = pageProps[APOLLO_STATE_PROP_NAME]; const store = useMemo(() => initializeApollo(state), [state]); return store; }