@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
TypeScript
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 {};