| name | tanstack-query-extension |
| description | Add the optional TanStack Query layer to @dudousxd/nestjs-codegen with tanstackQuery() from @dudousxd/nestjs-codegen-tanstack. Register it in NestjsCodegenModule.forRoot({ extensions: [tanstackQuery()] }) (or defineConfig). Point TanstackQueryOptions.import at your framework adapter (@tanstack/react-query default, or @tanstack/vue-query/-svelte-query/-solid-query); pageParamName names the infinite-query page field. Each api.ts leaf then exposes .queryOptions() (GET routes) / .mutationOptions() (writes) / .infiniteQueryOptions() (GET) / .queryKey(), while still being a plain awaitable request. Use when wiring TanStack Query, picking the framework import, or invalidating with queryKey(). |
| metadata | {"type":"core","library":"@dudousxd/nestjs-codegen-tanstack","library_version":"0.4.1","framework":"nestjs"} |
TanStack Query extension
TanStack Query is an extension, not a core flag. By default the generated client is a plain
typed fetch with no TanStack dependency. Register tanstackQuery() and each api.ts leaf
additionally carries queryOptions/mutationOptions/infiniteQueryOptions/queryKey from your
framework adapter — the leaf stays awaitable for direct requests.
Setup
pnpm add -D @dudousxd/nestjs-codegen-tanstack
pnpm add @tanstack/react-query
Register the extension where you configure the codegen (module or defineConfig file):
import { NestjsCodegenModule } from '@dudousxd/nestjs-codegen/nest';
import { zodAdapter } from '@dudousxd/nestjs-codegen-zod';
import { tanstackQuery } from '@dudousxd/nestjs-codegen-tanstack';
NestjsCodegenModule.forRoot({
contracts: { glob: 'src/**/*.controller.ts' },
codegen: { outDir: 'src/generated' },
validation: zodAdapter,
extensions: [tanstackQuery()],
});
Core patterns
1. Point import at YOUR framework adapter
You don't install @tanstack/query-core directly — your framework adapter re-exports
queryOptions/mutationOptions. Set TanstackQueryOptions.import to the package you have:
tanstackQuery();
tanstackQuery({ import: '@tanstack/vue-query' });
tanstackQuery({ import: '@tanstack/svelte-query' });
tanstackQuery({ import: '@tanstack/solid-query' });
Source: packages/tanstack/src/index.ts (TanstackQueryOptions.import, default '@tanstack/react-query'),
apps/docs/content/docs/client/tanstack-query.mdx.
2. GET → query helpers, writes → mutation helpers
The same leaf exposes different helpers by HTTP verb. GET routes get .queryOptions(),
.infiniteQueryOptions(), and .queryKey(); everything else gets .mutationOptions():
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() }),
});
const direct = api.users.show({ params: { id } });
}
queryKey() derives a stable key from the route name + input, so it's the canonical key for
invalidation. Each helper returns a real options object you spread your own onSuccess/select/
staleTime into.
Source: apps/docs/content/docs/client/tanstack-query.mdx, packages/tanstack/src/index.ts.
3. Configure the infinite-query page field
infiniteQueryOptions() appends a page param and reads the next page from response.meta.page /
response.meta.lastPage. The field name is structural (baked into the emitted queryFn), so set it
at generation time for cursor-style APIs:
tanstackQuery({ pageParamName: 'cursor' });
Source: packages/tanstack/src/index.ts (TanstackQueryOptions.pageParamName, default 'page').
Common mistakes
Calling .queryOptions() without registering the extension
NestjsCodegenModule.forRoot({ contracts, codegen, validation: zodAdapter });
useQuery(api.users.list().queryOptions());
NestjsCodegenModule.forRoot({ contracts, codegen, validation: zodAdapter, extensions: [tanstackQuery()] });
Without the extension the client is deliberately TanStack-free; the helpers only exist when
tanstackQuery() is in extensions.
Source: apps/docs/content/docs/client/tanstack-query.mdx ("an extension, not a core flag").
Leaving import on the React default in a Vue/Svelte/Solid app
tanstackQuery();
tanstackQuery({ import: '@tanstack/vue-query' });
The extension bakes the import string into the generated api.ts; the default is React, so
non-React apps must override it.
Source: packages/tanstack/src/index.ts (import option), apps/docs/content/docs/client/tanstack-query.mdx.
Expecting .mutationOptions() on a GET route (or .queryOptions() on a write)
useMutation(api.users.list().mutationOptions());
useQuery(api.users.list().queryOptions());
useMutation(api.users.create().mutationOptions());
The extension assigns helpers by verb: GET leaves carry the query helpers, every other verb carries
mutationOptions().
Source: apps/docs/content/docs/client/tanstack-query.mdx ("GET routes get queryOptions(), everything else mutationOptions()").