TanStack Query
queryOptions / mutationOptions from your framework adapter.
TanStack Query is an extension, not a core flag. By default the client is a plain typed
fetch with no TanStack dependency. Install the extension
and register it, and each endpoint returns a handle exposing TanStack's
queryOptions/mutationOptions helpers — GET routes get queryOptions(), everything
else mutationOptions().
pnpm add -D @dudousxd/nestjs-codegen-tanstackimport { tanstackQuery } from '@dudousxd/nestjs-codegen-tanstack';
NestjsCodegenModule.forRoot({
contracts: { glob: 'src/**/*.controller.ts' },
codegen: { outDir: 'src/generated' },
extensions: [tanstackQuery()],
});Which package?
You don't install @tanstack/query-core directly (nobody does) — your framework
adapter re-exports the helpers. Point the extension's import at the package you already
have (@tanstack/react-query is the default):
tanstackQuery(); // import defaults to '@tanstack/react-query'tanstackQuery({ import: '@tanstack/vue-query' });tanstackQuery({ import: '@tanstack/svelte-query' });tanstackQuery({ import: '@tanstack/solid-query' });Usage
The leaf is still awaitable — await api.users.list()
does a plain request — and the same handle exposes the TanStack helpers:
import { useQuery, useMutation, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
import { api } from '../lib/api';
function Users() {
const qc = useQueryClient();
const list = useQuery(api.users.list().queryOptions());
const pages = useInfiniteQuery(api.users.list().infiniteQueryOptions());
const create = useMutation({
...api.users.create().mutationOptions(),
onSuccess: () => qc.invalidateQueries({ queryKey: api.users.list().queryKey() }),
});
// …
}infiniteQueryOptions() (GET routes) adds page to the query and reads the next page from
response.meta.page / response.meta.lastPage.
Each handle builds a real queryOptions/mutationOptions object, so you compose it with
your own onSuccess, select, staleTime, etc. — and queryKey() derives a stable key
from the route name + input for you.