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.
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.
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 upgradeThe 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
