| name | new-hook |
| description | Scaffold a new data hook that wraps a generated TanStack Query operation, following this repo's src/hooks/<domain>/ convention. Use when the user says "add a hook", "create a useX hook", "wire up the <endpoint> API", "fetch <resource> with a hook", or "add a mutation/query for <thing>". NOT for non-data React hooks unrelated to the API (write those inline or ask). |
New Hook
Create a data hook under src/hooks/<domain>/ that wraps a
generated TanStack Query operation. The hook is a thin ergonomic layer: spread
the generated factory, add cache invalidation, return an async fn + the raw
mutation/query object.
Before writing
-
Read AGENTS.md (Data hooks section).
-
Find the generated operation. The client is generated by hey-api — never
hand-write request code. Locate the factory in the @tanstack/react-query.gen
barrel:
- App API:
@/client/@tanstack/react-query.gen
- Auth (allauth):
@/auth-client/@tanstack/react-query.gen
grep -rn "Mutation\|Options" src/client/@tanstack/react-query.gen.ts | head
If the operation doesn't exist, the OpenAPI schema may be stale — regenerate
with npm run openapi-ts (or npm run openapi-ts-auth) before continuing.
-
Pick the domain folder (authentication, organizations, or a new one)
and file name use-kebab-name.ts.
Patterns
Mutation hook
Mirror use-create-organization.ts:
import type { OrganizationWritable } from '@/client';
import { organizationsCreateMutation } from '@/client/@tanstack/react-query.gen';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { CURRENT_ORGANIZATION_QUERY_KEY } from './use-current-organization';
export function useCreateOrganization() {
const queryClient = useQueryClient();
const createOrganizationMutation = useMutation({
...organizationsCreateMutation(),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: CURRENT_ORGANIZATION_QUERY_KEY,
});
},
});
const createOrganization = async (body: OrganizationWritable) =>
createOrganizationMutation.mutateAsync({ body });
return { createOrganization, createOrganizationMutation };
}
Query hook
Spread the generated *Options() factory into useQuery, and export a
*_QUERY_KEY const so mutation hooks can invalidate it:
import { someResourceOptions } from '@/client/@tanstack/react-query.gen';
import { useQuery } from '@tanstack/react-query';
export const SOME_RESOURCE_QUERY_KEY = ['some-resource'] as const;
export function useSomeResource() {
return useQuery({ ...someResourceOptions() });
}
(Match the exact query-key shape the generated factory uses if you need precise
invalidation — check the .gen file.)
Conventions
- One hook per file. Named export
useCamelName. No default export.
- Types come from
@/client / @/auth-client — don't redeclare request/response shapes.
- Return both the ergonomic fn (
mutateAsync wrapper) and the raw object
(for isPending, error, etc. in the component).
- Add a short doc comment explaining the why, especially for auth/token side
effects (see
use-login.ts).
- Side effects on success (cache invalidation,
localStorage) go in onSuccess.
Verify
npm run typecheck and npm run lint clean.
- Add a test if the hook has non-trivial logic (token handling, header building,
branching) — mock the network like the auth page tests do.
Don't
- Don't write raw
fetch/axios — always go through the generated operation.
- Don't edit
src/client/ or src/auth-client/ to "add" an endpoint — regenerate.