UNPKG

@open-condo/apollo

Version:

A wrapper over @apollo/client that allows you to use persistent cache from local storage, configure TTL, invalidate cache, and use a single configuration for getServerSideProps, SSR, and CSR

177 lines (176 loc) 6.82 kB
import { ApolloClient } from '@apollo/client/core'; import { APOLLO_STATE_PROP_NAME } from '../constants'; import type { InitCacheConfig, InvalidationCacheConfig } from './cache'; import type { CachePersistor } from './cachePersistor'; import type { NormalizedCacheObject, ApolloCache, ApolloLink, RequestHandler } from '@apollo/client'; type ApolloClientOptions = { /** Apollo server url, can be static string or obtained from function on the fly */ uri: (() => string) | string; /** Headers to include in each request, can be helpful in SSR environments */ headers?: Record<string, string>; /** Client's cache config */ cacheConfig?: InvalidationCacheConfig; /** Middleware to process / enrich requests */ middlewares?: Array<ApolloLink | RequestHandler>; }; export type InitApolloClientOptions = Partial<Omit<ApolloClientOptions, 'cacheConfig'>> & { /** Results in cache being initialized with skipOnRead */ networkOnly?: boolean; }; export type ApolloHelperOptions = Omit<ApolloClientOptions, 'cacheConfig'> & { /** Cache initializer */ cacheConfig?: InitCacheConfig; }; type SSRResult<PropsType> = { props: PropsType & { [APOLLO_STATE_PROP_NAME]?: NormalizedCacheObject; }; }; type UseApolloHookResult<TClient> = { client: TClient; cachePersistor: CachePersistor<NormalizedCacheObject> | undefined; }; export type InitializeApollo<TClient> = (options?: InitApolloClientOptions) => TClient; export type UseApollo<TClient> = <PropsType>(pageProps: SSRResult<PropsType>['props']) => UseApolloHookResult<TClient>; interface ApolloHelperInterface { initializeApollo: InitializeApollo<ApolloClient<NormalizedCacheObject>>; generateUseApolloHook: () => UseApollo<ApolloClient<NormalizedCacheObject>>; } type ClientWithExtractableCache = { cache: Pick<ApolloCache<NormalizedCacheObject>, 'extract'>; }; /** * Extracts apollo cache from client and modify page props to include it, * so all data prefetched in SSR can be passed to client. * @example * export const getServerSideProps = async ({ req }) => { * const headers = extractHeaders(req) * const client = initializeApollo({ headers }) * * const { data } = await client.query({ ... }) * * return extractApolloState(client, { * props: { ... } * }) * } */ export declare function extractApolloState<PropsType>(client: ClientWithExtractableCache, pageParams: SSRResult<PropsType>): SSRResult<PropsType>; /** * Apollo helper which is used to generate utils for CSR / SSR, based on common client configuration * * @example Init helper and export utils for app * import { ListHelper, ApolloHelper } from '@open-condo/apollo' * import { TracingMiddleware } from '@open-condo/miniapp-utils/apollo' * import type { InitCacheConfig } from '@open-condo/apollo' * * const cacheConfig: InitCacheConfig = (cacheOptions) => { * const defaultListHelper = new ListHelper({ cacheOptions }) * * return { * typePolicies: { * Query: { * fields: { * allContacts: { * keyArgs: ['where'], * read: defaultListHelper.getReadFunction('paginate'), * merge: defaultListHelper.mergeLists, * }, * }, * }, * }, * } * } * * const apolloHelper = new ApolloHelper({ * middlewares: [ * new TracingMiddleware() * ], * uri: 'http://localhost:3000', * cacheConfig, * }) * * export const initializeApollo = apolloHelper.initializeApollo * export const useApollo = apolloHelper.generateUseApolloHook() * export { extractApolloState } from '@open-condo/apollo' * * @example Use in SSR * import { initializeApollo, extractApolloState } from '@/domains/common/utils/apollo' * * export const getServerSideProps = async ({ req }) => { * const headers = extractHeaders(req) * const client = initializeApollo({ headers }) * * const { data } = await client.query({ ... }) * * return extractApolloState(client, { * props: { ... } * }) * } * * @example Init on client in _app.tsx * export default function App ({ Component, pageProps }: AppProps): ReactNode { * const { client, cachePersistor } = useApollo(pageProps) * * return ( * <ApolloProvider client={client}> * <CachePersistorContext.Provider value={{ persistor: cachePersistor }}> * <Component {...pageProps} /> * </CachePersistorContext.Provider> * </ApolloProvider> * ) * } */ export declare class ApolloHelper implements ApolloHelperInterface { /** Initial options */ private readonly _initialApolloOptions; /** Cache initializer */ private readonly _cacheConfig?; /** Apollo client to reuse on CSR */ private _apolloClient; constructor(options: ApolloHelperOptions); /** * Generates cache config to init cache, * based on cache initializer and some client options */ private _getCacheConfig; /** * Creates new apollo client, if necessary with specified cache configuration * * When in SSR, generates new apollo client via createApolloClient util on each initializeApollo call * When in CSR, generate new apollo client once and then reuses it on subsequent initializeApollo calls * * You should probably use it only in SSR utils, such as getServerSideProps, * on client you should use useApollo hook instead * * @example Use in SSR * export const getServerSideProps = async ({ req }) => { * const headers = extractHeaders(req) * const client = initializeApollo({ headers }) * * const { data } = await client.query({ ... }) * * return extractApolloState(client, { * props: { ... } * }) * } */ initializeApollo(options?: InitApolloClientOptions): ApolloClient<NormalizedCacheObject>; /** * Generates useApollo hook to init apollo in react tree and pass it to whole app using default ApolloProvider * * @example * export default function App ({ Component, pageProps }: AppProps): ReactNode { * const { client, cachePersistor } = useApollo(pageProps) * * return ( * <ApolloProvider client={client}> * <CachePersistorContext.Provider value={{ persistor: cachePersistor }}> * <Component {...pageProps} /> * </CachePersistorContext.Provider> * </ApolloProvider> * ) * } */ generateUseApolloHook(): <PropsType>(pageProps: SSRResult<PropsType>['props']) => UseApolloHookResult<ApolloClient<NormalizedCacheObject>>; } export {};