server-state-tanstack-query
Conventions for fetching, caching, and mutating server state with TanStack Query — query key shape, Suspense vs plain queries, and optimistic updates.
Files1
View on GitHubSKILL.md.claude/skills/server-state-tanstack-query/SKILL.md
GitHub- Query keys are arrays, most-generic segment first, defined once as a
const ... as constnext to the query itself (seeSUBSCRIBERS_KEYinsrc/lib/queries.ts) — never inline a raw array literal at each call site, or cache invalidation will silently miss. - Default to
useSuspenseQuery(notuseQuery) for data a component cannot render without — it removes theisLoading/data is possibly undefinedbranch entirely at the type level. Wrap the consuming component in<Suspense fallback={...}>one level up (seesrc/routes/index.tsx). Use plainuseQueryonly when the component has a meaningful "no data yet" UI state of its own. - Mutations validate their input against the same Zod schema used at the API boundary
(
src/lib/schemas.ts) insidemutationFn, not in the calling component — the mutation hook is the trust boundary, not the form. - On mutation success, prefer
queryClient.setQueryDatawith the server's actual response (seeuseSubscribeMutation) overinvalidateQuerieswhen the mutation response already contains the new state — one fewer network round-trip. - Every fetch goes through
fetch()against a relative/api/...path with an MSW handler insrc/mocks/handlers.tsbacking it in dev and tests (testing-rtl-mswskill) — never call an absolute external URL directly from a component or hook.