query

Load data from your API, keep it cached, and refresh it when something changes.

Data fetchingClientBuilt on @tanstack/react-query

kitr/query re-exports everything from @tanstack/react-query and adds the following.

defineQuery

function defineQuery<TInput = void, TOutput = unknown>(contract: {
  key: (input: TInput) => QueryKey
  fetch: (input: TInput, ctx: { signal?: AbortSignal }) => Promise<TOutput>
  staleTime?: number
  source?: string
}): QueryDefinition<TInput, TOutput>
Member Description
options(input) TanStack queryOptions for useQuery, useSuspenseQuery, ensureQueryData. Errors are normalised to KitrError.
key(input) The cache key.
fetch(input, signal?) Call the source directly, bypassing the cache.
contract The full contract, for tooling.

defineMutation

function defineMutation<TInput = void, TOutput = unknown>(contract: {
  mutate: (input: TInput) => Promise<TOutput>
  effects?: CacheEffect | ((result: TOutput, input: TInput) => CacheEffect)
  source?: string
}): MutationDefinition<TInput, TOutput>
Member Description
options({ queryClient }) TanStack mutation options that apply effects on success.
mutate(input) Call the source directly, without effects.
effectsFor(result, input) The effects for one result.

useKitrMutation

function useKitrMutation<TInput, TOutput>(
  definition: MutationDefinition<TInput, TOutput>,
  overrides?: Omit<UseMutationOptions<TOutput, Error, TInput>, 'mutationFn'>,
): UseMutationResult<TOutput, Error, TInput>

useMutation with effects applied to the active QueryClient. Your own onSuccess runs after the effects.

defineSubscription and useSubscription

function defineSubscription<TInput = void, TEvent = unknown>(contract: {
  key: (input: TInput) => QueryKey
  subscribe: (input: TInput, handlers: { next(event: TEvent): void; error?(e: KitrErrorShape): void }) => () => void
  effects?: (event: TEvent, input: TInput) => CacheEffect | void
}): SubscriptionDefinition<TInput, TEvent>

function useSubscription<TInput, TEvent>(
  definition: SubscriptionDefinition<TInput, TEvent>,
  input: TInput,
  options?: { enabled?: boolean; onEvent?: (event: TEvent) => void; onError?: (error: KitrErrorShape) => void },
): { status: 'idle' | 'open' | 'error'; lastEvent?: TEvent; error?: KitrErrorShape }

CacheEffect

interface CacheEffect {
  invalidate?: QueryKey[]                       // prefix match
  set?: Array<{ key: QueryKey; data: unknown }> // write without refetching
  remove?: QueryKey[]
}

applyCacheEffect and invalidate

applyCacheEffect(queryClient, effect): Promise<void>
invalidate(queryClient, ...keys): Promise<void[]>

For applying effects outside a mutation, such as in a test or a custom event handler.

createQueryClient and defineConfig

createQueryClient(config?: QueryConfig): QueryClient
defineConfig(config: QueryConfig): QueryConfig

The provider calls createQueryClient with config/query.ts and binds it as queryClient in the container and in router context.