All files / lib query-client.ts

100% Statements 11/11
100% Branches 12/12
100% Functions 6/6
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48                          1x 3x           1x 10x   1x 6x     5x 2x           5x             4x            
import { MutationCache, QueryCache, QueryClient } from '@tanstack/react-query';
 
import { type ApiError, InvalidResponseError, NetworkError, ServerError } from '@/lib/api/errors';
import { reportError } from '@/lib/report-error';
 
declare module '@tanstack/react-query' {
  interface Register {
    /** `runEffect` rejects with an `ApiError`, so that is the type of every `query.error`. */
    defaultError: ApiError;
  }
}
 
/** Only failures that can succeed on a second try are retried; 4xx answers will not change. */
const isTransient = (error: ApiError) =>
  error instanceof NetworkError || (error instanceof ServerError && error.status >= 500);
 
/**
 * A bug, not a state the screen already explains: the server failed (5xx) or answered with a body
 * that breaks its schema. A 4xx or a lost connection is the user's to see, not to report.
 */
const isUnexpected = (error: ApiError) =>
  error instanceof InvalidResponseError || (error instanceof ServerError && error.status >= 500);
 
export const createQueryClient = () =>
  new QueryClient({
    queryCache: new QueryCache({
      onError: (error, query) => {
        if (isUnexpected(error)) {
          reportError(error, { source: 'query', where: JSON.stringify(query.queryKey) });
        }
      },
    }),
    mutationCache: new MutationCache({
      onError: (error) => {
        if (isUnexpected(error)) reportError(error, { source: 'mutation' });
      },
    }),
    defaultOptions: {
      queries: {
        // Avoid refetching the same data on every route change.
        staleTime: 30_000,
        retry: (failureCount, error) => failureCount < 1 && isTransient(error),
      },
      // Mutations are not idempotent: never retry them automatically.
      mutations: { retry: 0 },
    },
  });