Blog / 01

NestJS 12 Ships: ESM, Standard Schema, Observability

WebRestart

On August 27, 2026 Kamil Myśliwiec published NestJS v12.0.0. The major that had been tracking "approximately Q3 2026" under the next tag is now stable, and it lands almost exactly as scoped: every official package ships as ESM, Standard Schema validation and serialization are built into the framework, the CLI has been rebuilt, and there is a first-party observability SDK. There is also a nest upgrade command that does most of the migration for you.

If you read our NestJS 12 roadmap post in June, the shape is familiar. What changed between the draft PR and the release is mostly in the details — and the details are where the upgrade work lives.

ESM, and the Node floor that comes with it

All core packages are now ECMAScript modules. Existing CommonJS applications keep working through require(esm), so migrating your own source to ESM stays optional. What is not optional is the runtime: v12 requires Node.js v20.19+ or v22.12+, and the 21.x line is not supported. Check your Dockerfiles and CI images before anything else — that is the constraint most likely to stop the upgrade cold.

The parts that need a real look are custom bootstrapping and build configuration. If you have hand-rolled Webpack config, a custom main.ts entry, or anything that reaches into Nest's internals through require, budget time for it.

Standard Schema, in both directions

Route parameter decorators — @Body(), @Query(), @Param() and @RawBody() — now accept a schema option that takes any Standard Schema-compatible object, which means Zod, Valibot and ArkType are first-class without a wrapper library. The one thing the roadmap post could not tell you: you have to register the new StandardSchemaValidationPipe for it to do anything.

The genuinely new half is serialization. StandardSchemaSerializerInterceptor validates outgoing responses against the same schemas:

@SerializeOptions({ schema: userResponseSchema })
@Get(':id')
findOne(@Param('id') id: string) {
  return this.users.findOne(id);
}

That closes the gap class-transformer never quite covered — a response shape you can assert on rather than hope for. @nestjs/config moves the same way: it drops Joi-specific validation for Standard Schema, so validationSchema accepts any compatible schema. If you are staying on Joi, it must be v18 or later.

@nestjs/observe

The new observability SDK plugs into Nest's own request lifecycle through the instrument application option rather than monkey-patching the HTTP server the way a generic APM agent does. It covers HTTP, GraphQL, gRPC, microservices, queues and cron jobs without per-handler instrumentation.

The distinction matters in practice: an agent that patches the server sees a request and a response, while something wired into the lifecycle can attribute time to a guard, an interceptor or a specific provider. For anyone who has tried to work out why p99 moved on a Nest API, that is the difference between a number and an answer.

The CLI, and the toolchain that actually shipped

@nestjs/cli v12 adds nest upgrade to migrate v11 projects, and nest deploy for Mau. Rspack is the new default bundler for monorepos; the --webpack and --webpackPath flags are deprecated in favour of --builder rspack. oxlint replaces ESLint, and bun joins the supported package managers.

One correction to what we wrote in June: Vitest is the default only for ESM projects. CommonJS projects continue with Jest. If you take the ESM path and Jest you will be on your own.

Breaking changes worth checking first

Beyond ESM and the Node floor, the migration table calls out several that will bite specific stacks:

  • NATS: npm uninstall nats && npm install @nats-io/transport-node, then update imports
  • GraphQL: subscriptions-transport-ws support is removed — migrate to graphql-ws; GraphiQL is now the default IDE, so replace playground with graphiql
  • Lifecycle hooks now run by hierarchy level, so review provider and module ordering
  • ConsoleLogger treats plain objects after a message as structured params; set structuredParams: false to keep the old behaviour
  • The angular schematic has been removed

What to do now

Nothing urgent. This is a major with real breaking changes, not a security patch, and 11.x is still getting work — 11.2.2 and 11.2.3 both landed on August 25 with bug fixes. Staying on 11 through the next sprint is a defensible call.

When you do move, start with the runtime. Get onto Node 20.19+ or 22.12+ first, as its own change, then:

npm i -g @nestjs/cli@latest
nest upgrade --dry-run

--dry-run prints the changes without applying them, which makes it a cheap way to size the job before committing to it. Read that diff against the list above — the codemod handles the mechanical parts, not your NATS transport or your GraphQL subscription layer.

Full details are in the v12.0.0 release notes and the original scope PR #16391.


Sitting on a Nest 11 codebase with custom Webpack config and a NATS transport, and unsure what the upgrade actually costs? Talk to us.