NestJS 12: ESM Core Packages, CJS Compatibility & New Toolchain Defaults

NestJS 12 releases core packages as ESM while preserving CommonJS compatibility via Node.js's require(esm), introduces Vitest and oxlint as defaults for new ESM projects, adopts Rspack for monorepos, adds Standard Schema support for validation and serialization, and includes an official observability SDK with distributed tracing.

Node.js Tech Stack
Node.js Tech Stack
Node.js Tech Stack
NestJS 12: ESM Core Packages, CJS Compatibility & New Toolchain Defaults

NestJS 12 Released: ESM Core Packages with CommonJS Compatibility

NestJS founder Kamil Myśliwiec announced v12 on August 28, 2024, alongside a redesigned official website and documentation — the first major site overhaul in nearly nine years.

ESM Packages Without Forcing Application Migration

The core @nestjs/* packages are now published as ESM. However, NestJS does not require application code to migrate. Modern Node.js supports require(esm) to load ESM packages from CommonJS projects, and nest upgrade preserves the project's existing module format. A long-running CJS service can upgrade Nest dependencies first and schedule its own ESM migration separately.

This compatibility depends on Node.js versions. Running a NestJS 12 application requires at least Node.js 20.19; with Node.js 22, at least 22.12 is needed. The schematics used by nest new, nest generate, and nest upgrade have stricter requirements: Node.js 22.22.3+, 24.15+, or 26. Teams should align development machines and CI to a qualifying active LTS version to avoid scaffolding failures after dependencies install.

New Project Toolchain Defaults: Vitest, oxlint, and Rspack for Monorepos

When running nest new, the CLI prompts for CommonJS or ESM. New ESM projects default to Vitest and oxlint, while CommonJS templates continue with Jest and ESLint. Existing Jest projects need not migrate; @nestjs/testing remains test-framework agnostic.

Rspack becomes the default bundler for monorepos, and webpack-related options in Nest CLI enter deprecation. Regular projects still default to tsc. Teams using webpack plugins, custom webpack.config, or relying on webpack-specific behavior should prioritize verification.

This approach mirrors NestJS's typical strategy: new projects adopt currently prevalent tooling, while existing projects keep their own pace without forcing a rewrite of stable Jest tests and CommonJS builds.

Standard Schema Integration Across Validation, Serialization, and Configuration

Previously previewed, @Body(), @Query(), and @Param() now accept Standard Schema-compatible schemas (Zod, Valibot, ArkType, etc.), validated by the built-in StandardSchemaValidationPipe. Example:

@Post()
create(@Body({ schema: createUserSchema }) body: CreateUserDto) {
  return this.usersService.create(body);
}

Response serialization gains StandardSchemaSerializerInterceptor, and @nestjs/config 's validationSchema is no longer tied to Joi. Teams already using Zod can reduce maintenance of parallel class DTOs and validation rules. class-validator and class-transformer remain supported with no deprecation plans. Mature projects can safely continue the decorator approach; new modules or those needing frontend-backend schema sharing are better suited for Standard Schema. Joi users must upgrade to v18+ and check validationOptions.libraryOptions configuration placement.

Practical Improvements for Day-to-Day Debugging

The author highlights several lower-profile updates that address recurring operational pain points:

Structured logging via ConsoleLogger: Plain objects passed after the log message are treated as structured parameters. JSON logs can place these fields in params or flatten them to the top level with flattenParams, eliminating custom wrappers for log platforms and making fields like userId and HTTP method easily searchable. Example:

this.logger.log('User signed in', {
  userId: 1,
  method: 'oauth',
});

Stable error codes in HttpExceptionOptions: New errorCode field lets clients handle branches by stable codes instead of parsing mutable error messages.

Framework-level route conflict diagnostics: routeConflictPolicy handles duplicate routes and path shadowing; routeResolutionStrategy selects routes by specificity. For example, @Get(':id') placed before @Get('me') could silently match incorrectly in Express; now teams can opt into warnings or errors. Both default to legacy behavior and must be explicitly enabled.

Official Observability SDK: @nestjs/observe

NestJS 12 introduces the official @nestjs/observe SDK, integrating with controllers, interceptors, GraphQL resolvers, and queue consumers to collect requests, background tasks, errors, and logs. The official console displays request volume, latency, error rates, and performance anomalies. Distributed tracing is included: a single request spanning database, search service, and payment API can be viewed in a waterfall chart with per-segment timings and logs.

NestJS Observe project console
NestJS Observe project console
NestJS Observe distributed tracing waterfall
NestJS Observe distributed tracing waterfall

For teams with existing OpenTelemetry setups, migration depends on current investment. New Nest projects adding observability gain a framework-lifecycle-integrated option.

Upgrade Process: nest upgrade Helps but Doesn't Automate Everything

The CLI now provides a formal upgrade workflow. Recommended steps:

npm i -g @nestjs/cli@latest @nestjs/schematics@latest
npm i -D @nestjs/cli@latest @nestjs/schematics@latest

nest upgrade --dry-run
nest upgrade

The command updates all @nestjs/* dependencies, migrates mechanical configurations (Rspack config, GraphQL options, NATS packages, and some @nestjs/config settings), and reports changes it cannot handle automatically. It does not convert projects to ESM or migrate to Vitest/oxlint.

Behavioral changes to watch: NATS moves to v3 client; GraphQL subscriptions switch to graphql-ws; lifecycle hooks are invoked by component hierarchy; custom Pipe type signatures become stricter. Any of these can make "dependencies installed" differ from "service behavior unchanged."

Author's Upgrade Recommendation

New projects can confidently start on v12. For production legacy projects: first raise Node.js to a supported version range, run nest upgrade --dry-run in an isolated branch, then focus regression testing on startup/shutdown flows, messaging services, GraphQL, and logging. Treat ESM, Vitest, and oxlint migrations as separate tasks, not bundled into a single major-version upgrade.

NestJS 12's final release delivers more pragmatic answers than its preview: default tooling moves forward while existing projects retain an exit path. For Node.js service maintainers, this is the kind of major version that's actually practical to adopt.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

RspackCommonJSESMNestJSVitestoxlintNestJS 12Standard Schema
Node.js Tech Stack
Written by

Node.js Tech Stack

Focused on sharing AI, programming, and overseas expansion

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.