To avoid scattered and error-prone cache keys, the project uses a Hierarchical Query Factory. This pattern centralizes all query keys into a single, type-safe object and provides corresponding queryOptions for fetching data.
This approach ensures:
- Single source of truth: Keys are defined once.
- Type-safety: TypeScript autocomplete for keys and query functions.
- Hierarchical structure: Keys follow a predictable path (e.g.,
flows → detail → nodes), allowing for granular or broad cache invalidation. - No typos: Prevents bugs caused by manual string/array key entry in components.
// app/queries/flow/query-factory.ts
export const flowKeys = {
all: ['flows'] as const,
lists: () => [...flowKeys.all, 'list'] as const,
list: (filters?: FlowFilters) => [...flowKeys.lists(), { filters }] as const,
details: () => [...flowKeys.all, 'detail'] as const,
detail: (id: string) => [...flowKeys.details(), id] as const,
nodes: (flowId: string) => [...flowKeys.detail(flowId), 'nodes'] as const,
node: (flowId: string, nodeId: string) => [...flowKeys.nodes(flowId), nodeId] as const,
}
export const flowQueries = {
detail: (id: string) => queryOptions({
queryKey: flowKeys.detail(id),
queryFn: async () => {
const flow = await FlowService.getById(id);
if (!flow) throw new Error('Flow not found');
return flow;
},
staleTime: 1000 * 10,
}),
nodes: (flowId: string) => queryOptions({
queryKey: flowKeys.nodes(flowId),
queryFn: async () => FlowService.getNodes(flowId),
staleTime: 1000 * 5,
}),
}