Inertia
A TypeScript-first Inertia.js adapter for NestJS — multi-app, Vite-native, with a Tuyau-style typed client.
@dudousxd/nestjs-inertia brings the Inertia.js protocol to NestJS without giving up TypeScript. You render a page by returning a component name and props from a normal controller — @Inertia('Home') — and the adapter speaks the Inertia protocol over the wire, serves your Vite-built frontend, and (optionally) hands your React, Vue, or Svelte client a fully typed map of every route, query, and response derived straight from your controllers.
The result is a single-page app with server-side routing: no REST contract to maintain by hand, no client/server type drift, and no second build pipeline to babysit. You write a controller, you write a page component, and the types between them stay in sync.
Inertia.js is the "modern monolith" pattern — your server controllers stay the source of truth for routing and data, while your pages are real client-side components. This adapter is the NestJS half of that contract.
Quickstart
The whole loop — install, scaffold, render a page — in three steps. For the full walkthrough (SSR, Docker, manual setup, options), see Getting Started.
Install the suite, plus your framework's Inertia adapter and Vite plugin (React shown here):
pnpm add @dudousxd/nestjs-inertia @dudousxd/nestjs-inertia-vite @dudousxd/nestjs-inertia-client
pnpm add -D @dudousxd/nestjs-codegen @dudousxd/nestjs-inertia-codegen-extension @dudousxd/nestjs-inertia-testing
pnpm add @inertiajs/react@^3.0.0 react react-dom
pnpm add -D @types/react @types/react-dom @vitejs/plugin-reactRun init — it detects your frontend framework and template engine, scaffolds the HTML shell, Vite config, entry point, a sample page, and auto-patches your app.module.ts and main.ts:
pnpm exec nestjs-inertia initEvery create and patch is idempotent, so it's safe to re-run. If init can't parse your bootstrap files, it prints the exact snippets to add by hand — InertiaModule.forRoot({ rootView }) in app.module.ts and setupInertiaVite(app, {...}) in main.ts.
Render a page from any controller — return the component name and props, and the adapter handles the protocol:
import { Controller, Get } from '@nestjs/common';
import { Inertia } from '@dudousxd/nestjs-inertia';
@Controller()
export class HomeController {
@Get()
@Inertia('Home')
home() {
return { message: 'Hello from NestJS' };
}
}Run nest start --watch and open http://localhost:3000 — done.
Typed client is opt-in
The render loop above is all you need to ship. When you want end-to-end types, codegen runs automatically under nest start --watch and emits a typed page map, route constants, and a TanStack-Query-powered api.ts into .nestjs-inertia/. See the Typed Client and Codegen guides.
What it is
The adapter is built as a small suite of packages, so you only pull in what you use:
@dudousxd/nestjs-inertia— the core protocol andInertiaModule. Speaks the Inertia request/response contract, manages shared props, asset versioning, partial reloads, and SSR.@dudousxd/nestjs-inertia-vite— Vite dev-server middleware in development and static-manifest serving in production, wired with one call tosetupInertiaVite.@dudousxd/nestjs-inertia-client— the Tuyau-style typed client: a typed<Link>with route-name autocomplete andcreateFetcherfor SSR-safe data fetching.@dudousxd/nestjs-inertia-codegen-extension+@dudousxd/nestjs-codegen— read your existing NestJS decorators (@Controller,@Get,@Query,@ApiResponse,@As) and generate typed pages, routes, and anapi.tsclient. No Zod, no contracts, no extra schemas required.@dudousxd/nestjs-inertia-testing—expectInertia()matchers and a testing harness for asserting on Inertia responses.
The problem it solves
A conventional NestJS + SPA setup means running two worlds in parallel: REST controllers on one side, a hand-written API client and route table on the other, and a contract between them that only TypeScript can't see. Every new endpoint is a place for the two to drift.
Inertia collapses that gap — the server stays in charge of routing and data — and this adapter makes the NestJS side first-class:
- TypeScript-first. Page props, route names, query shapes, and response types are derived from your controllers, not declared twice. Change a controller signature and the client types follow.
- Vite-native. HMR and React Refresh in development, hashed manifest assets in production, with the dev-server-versus-static switch handled for you.
- Multi-app.
InertiaModule.forFeature(...)lets a single NestJS process serve several independent frontends — an admin panel and a storefront, say — each with its own pages and assets.
Where to go next
Getting Started
Install, run init, and render your first page — plus SSR, Docker, and the full forRoot options.
Guides
Installation, multi-app, codegen, the typed client, and testing in depth.
Recipes
Copy-paste solutions: auth-redirect guard and a not-found exception filter.
Packages
Per-package API reference for core, vite, client, codegen, and testing.
Architecture
Package responsibilities and the Inertia request lifecycle.