Skip to content

Query And Mutation Template

Migrated from root technical docs.

Use this when adding server state to a feature.

The owning feature owns:

  • query keys
  • query option factories
  • mutation hooks
  • invalidation behavior
import type { {Feature}Search } from "./types";
export const {feature}QueryKeys = {
all: ["{feature}"] as const,
list: (search: {Feature}Search) => ["{feature}", search] as const,
detail: (id: string) => ["{feature}-detail", id] as const,
} as const;

Rules:

  • keep keys serializable
  • use typed object params for list keys
  • keep detail keys separate from list keys
import { queryOptions } from "@tanstack/react-query";
import { getResponseStatus, isRetryableStatus } from "@legaciti/platform-query";
import { list{Feature}Items, get{Feature}Detail } from "./api";
import { {feature}QueryKeys } from "./query-keys";
export const {feature}ListQueryOptions = (search: {Feature}Search) =>
queryOptions({
queryKey: {feature}QueryKeys.list(search),
queryFn: async () => list{Feature}Items(search),
placeholderData: (previous) => previous,
retry: (failureCount, error: unknown) => {
const status = getResponseStatus(error);
return failureCount < 2 && isRetryableStatus(status);
},
});
export const {feature}DetailQueryOptions = (id: string) =>
queryOptions({
queryKey: {feature}QueryKeys.detail(id),
queryFn: async () => get{Feature}Detail(id),
staleTime: 0,
retry: false,
});
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { emitCacheInvalidations } from "@legaciti/platform-events";
import { update{Feature}Item } from "./api";
import { {feature}QueryKeys } from "./query-keys";
export function useUpdate{Feature}Mutation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: update{Feature}Item,
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({ queryKey: {feature}QueryKeys.all });
queryClient.invalidateQueries({
queryKey: {feature}QueryKeys.detail(variables.id),
});
emitCacheInvalidations([
{ queryKey: {feature}QueryKeys.all },
{ queryKey: {feature}QueryKeys.detail(variables.id) },
]);
},
});
}
  • raw write transport functions stay in api.ts
  • mutations.ts owns cache update behavior
  • do not define ad hoc query keys inline inside components
  • do not statically import server function modules into client-safe files