Skip to Content

Install

Nestia runs on TypeScript 7 through ttsc. Install the compiler toolchain, the runtime packages, and the CLI explicitly:

Terminal
npm i -D ttsc typescript npm i typia @nestia/core @nestia/sdk @nestia/fetcher npm i -D nestia

@nestia/sdk is a runtime dependency. The generated SDK and runtime Swagger composition both resolve it while the Nestia transform attaches route metadata.

ttsc must be 0.19.2 or newer. ttsc compiles the Nestia transform from Go source on first use, and that source reports the compiled project’s reference graph through an API older releases do not have, so an older ttsc fails the plugin build instead of silently producing a transform without it. @nestia/core declares the floor as an optional peer dependency.

tsconfig.json

Keep the NestJS decorator metadata flags. Do not add compilerOptions.plugins for normal Nestia setup; ttsc discovers @nestia/core, @nestia/sdk, and typia from their package manifests.

tsconfig.json
{ "compilerOptions": { "strict": true, "experimentalDecorators": true, "emitDecoratorMetadata": true } }

Transform options

No plugin config means Nestia uses its defaults:

  • @TypedBody validates with "validate".
  • @TypedRoute serializes with "assert".
  • LLM schema restrictions are off.

Add compilerOptions.plugins only when you want to override those options:

tsconfig.json
{ "compilerOptions": { "plugins": [ { "transform": "typia/lib/transform", "enabled": false }, { "transform": "@nestia/core/native/transform.cjs", "validate": "validatePrune", "stringify": "validate.log", "llm": { "strict": true } } ] } }

If a nest new project has tsconfig.build.json, point ttsc at that file so test files stay out of production output:

package.json
{ "scripts": { "build": "ttsc -p tsconfig.build.json" } }

Build

Replace NestJS CLI build commands with ttsc / ttsx.

package.json
{ "scripts": { "build": "ttsc", "start": "node dist/main.js", "start:dev": "ttsc && concurrently \"ttsc --watch\" \"node --watch dist/main.js\"", "start:script": "ttsx src/main.ts" } }

nest build, nest start, tsc, ts-node, and tsx do not run the Nestia transform. Use ttsc for builds and ttsx for direct TypeScript execution.

start:dev builds once before starting the watchers on purpose. concurrently starts both commands immediately, and node --watch exits with Cannot find module if its entry does not exist yet — it only begins watching after a successful first load — so without the leading build it loses the race against the initial ttsc --watch compile and never comes back.

Plugin cache

ttsc compiles the Nestia transform from Go source, which is why the first build reports building source plugin "@nestia/core". It is compiled once per cache key and reused afterwards; the message appears again only when something in that key changes.

Two caches decide whether that work repeats. The compiled plugin lives in TTSC_CACHE_DIR, defaulting to <workspace>/node_modules/.cache/ttsc, and the Go build cache lives in TTSC_GO_CACHE_DIR or GOCACHE. Neither falls back to a location outside the workspace, so rm -rf node_modules reclaims both — and discards both.

The cache key covers the ttsc and TypeScript-Go versions, the platform, the Go toolchain, and the plugin’s own source. It does not cover your application code. A container image that installs dependencies in one layer and copies sources in a later one can therefore warm the plugin during the dependency layer and keep it across every source change, or mount both caches:

Dockerfile
RUN --mount=type=cache,target=/ttsc-cache \ --mount=type=cache,target=/go-cache \ TTSC_CACHE_DIR=/ttsc-cache TTSC_GO_CACHE_DIR=/go-cache pnpm build

ttsc needs a Go toolchain on PATH; point TTSC_GO_BINARY at an absolute path when the image keeps it somewhere unusual.

Generate

Create nestia.config.ts once, then run the generators:

Terminal
npx nestia init npx nestia sdk npx nestia swagger npx nestia e2e npx nestia all

init writes a starter config. sdk, swagger, and e2e emit one artifact each; all runs the three together.

Bundlers

A normal NestJS server should compile with ttsc directly. When a bundler owns the build, install @ttsc/unplugin and add the adapter for that bundler.

Terminal
npm i -D @ttsc/unplugin
vite.config.ts
import ttsc from "@ttsc/unplugin/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [ttsc()], });

Use the matching import path for other bundlers: @ttsc/unplugin/webpack, @ttsc/unplugin/rspack, @ttsc/unplugin/esbuild, @ttsc/unplugin/next, @ttsc/unplugin/rollup, @ttsc/unplugin/rolldown, @ttsc/unplugin/farm, or @ttsc/unplugin/bun.

A persistent bundler cache stays correct across builds without any extra configuration. A bundler erases type-only imports from its own module graph, so nothing in that graph connects a controller’s generated validator to the DTO declaration it was generated from; the Nestia transform reports the compiler’s own reference graph to the adapter, which registers the DTO as an input of the controller module. Editing the DTO’s type rebuilds the controller instead of replaying the cached module, in watch mode and in a kept filesystem cache alike.

Troubleshooting

  • no transform has been configured: the project was built by tsc, nest build, SWC, Babel, or another path that skipped ttsc.
  • Empty SDK or Swagger output: confirm the controllers use @TypedRoute and that the generator reads the same tsconfig.json as the build.
  • NestJS DI fails: restore experimentalDecorators and emitDecoratorMetadata; NestJS still needs them for constructor injection.
  • Bundler output skips validation: add the matching @ttsc/unplugin adapter to the bundler config.
  • Plugin build fails on an unknown Go symbol: ttsc is older than 0.19.2. Upgrade it; the version floor is listed under Install.
Last updated on