Full type safety from controller to client.
Point it at your NestJS controllers and DTOs. Out come typed routes, a typed API client, and validation schemas — with pluggable validation (zod / valibot / arktype), optional TanStack Query, and a bring-your-own fetcher. Works with or without Inertia.
zod · valibot · arktype · TanStack Query · axios / superjson · works without Inertia
// hand-written client, drifts from the APIexport async function getUser(id: string) { const res = await fetch(`/users/${id}`); return res.json() as User; // 🤞}// ...repeat for every endpoint, by handimport { createApi } from './generated/api';import { createFetcher } from '@dudousxd/nestjs-client'; const api = createApi(createFetcher({ baseUrl: '/api' })); // typed params + response, no driftconst user = await api.users.show({ params: { id } }); // opt in to TanStack QueryuseQuery(api.users.show({ params: { id } }).queryOptions());Typed wire, your stack
Everything between your NestJS controllers and your client is generated and type-checked — and every layer is yours to swap.
Pluggable validation
One neutral schema IR → zod (bundled), valibot, or arktype. Standard-Schema-shaped, so adding a lib is one render function. Your DTOs are the source of truth.
Typed API client
A Tuyau-style createApi(fetcher) factory, nested by route name. Params, body, and response inferred from your controllers — wrong calls fail at compile time.
TanStack Query, opt-in
By default each endpoint is a plain typed fetch. Turn on query and the same call exposes .queryOptions() / .mutationOptions() from your own @tanstack adapter.
Bring your own fetcher
Native fetch by default, or plug an axios instance via axiosTransport(). superjson and transformer pipelines preserve Date, Map & friends end-to-end.
nestjs-inertia integration
Optionally discover Inertia pages and shared props, augment InertiaPages, and emit a typed router navigate() — the same codegen, with Inertia switched on.
nestjs-filter integration
Discovers @FilterFor / @ApplyFilter and emits a TypedFilterQuery over your filterable fields, wired to @dudousxd/nestjs-filter-client.
Import the module. It runs itself.
Add NestjsCodegenModule.forRoot() to your AppModule and the codegen starts with your dev server — the typed client regenerates as you edit your controllers. No config file, no extra process.
Shipping? The same generator ships as a CLI — run nestjs-codegen codegen in CI before you deploy so the committed client can never drift from your routes.
import { Module } from '@nestjs/common';import { NestjsCodegenModule } from '@dudousxd/nestjs-codegen/nest'; @Module({ imports: [ NestjsCodegenModule.forRoot({ contracts: { glob: 'src/**/*.controller.ts' }, codegen: { outDir: 'src/generated' }, }), ],})export class AppModule {}# CI / pre-deploy — fail the build if the client drifts$ npx nestjs-codegen codegen✓ routes.ts · api.ts · forms.ts regeneratedStop hand-writing your API client.
Generate it from the controllers you already have — typed end to end, with the validation lib, fetcher, and query layer of your choice.