NestJS 12 dropped last week with ESM packages, Standard Schema validation, a rebuilt CLI, and a brand-new observability SDK. That last one is the most interesting part for anyone running NestJS in production with an APM tool.
At Scout Monitoring, we instrument NestJS applications through our Node.js agent. A framework shipping its own observability layer changes the conversation for every APM vendor in the ecosystem, including us. Here is what you need to know.
What Changed in NestJS 12
This is a big major release. The highlights, in order of how much they’ll affect your upgrade:
Node.js v20.19+ is now required. NestJS 12 drops support for anything older, including the entire v21.x line. Both the ESM packages and require(esm) depend on modern Node.js. If you’re still on v18, you need to upgrade Node before you upgrade Nest.
All core packages ship as ESM. Existing CommonJS applications keep working thanks to require(esm) in modern Node.js. You don’t need to rewrite your app. But check your build tooling, test runners, and any custom bootstrap scripts that assume CommonJS-only packages. The nest new command now asks whether to scaffold a CommonJS or ESM project.
Standard Schema validation replaces the class-validator monopoly. Route parameter decorators like @Body(), @Query(), and @Param() now accept a schema option that works with Zod, Valibot, and ArkType through the Standard Schema spec. Register StandardSchemaValidationPipe globally and your schemas validate at the decorator level:
@Post()
create(@Body({ schema: createUserSchema }) body: CreateUserDto) {
return this.usersService.create(body);
}
The class-validator workflow still works. No deprecation, no removal planned. Pick whichever approach fits your codebase.
The config module moved to Standard Schema too. @nestjs/config now accepts any Standard Schema-compatible library for environment validation. If you’re using Joi, upgrade to Joi v18+ (the first version implementing Standard Schema) and move library-specific settings under validationOptions.libraryOptions.
The CLI got rebuilt. The new nest upgrade command moves every @nestjs/* package to its v12-compatible version and applies mechanical migration steps automatically. Run it with --dry-run first:
npm i -g @nestjs/cli@latest
nest upgrade --dry-run
It handles the nest-cli.json webpack options, the GraphQL playground to graphiql rename, NATS package replacement, and Jest/Joi version bumps. It does not migrate you to ESM, Vitest, or oxlint. Those are defaults for new projects only.
The Observability Play: @nestjs/observe
This is the change that matters most for APM users. NestJS 12 ships an official observability SDK called @nestjs/observe that hooks directly into Nest’s request lifecycle.
Setup is two lines:
export const { ObserveModule, ObserveInstrument } = createObserveModule();
const app = await NestFactory.create(AppModule, {
instrument: ObserveInstrument,
});
The instrument application option lets @nestjs/observe plug into the framework internals rather than wrapping the HTTP server from the outside. It auto-instruments HTTP requests, GraphQL resolvers, gRPC calls, microservice transports, queue consumers, and cron jobs. No collector process to run. No manual span wiring.
The nest new and nest upgrade commands can wire it up for you with the --observe flag.
What Framework-Native Instrumentation Gets Right
There are real advantages to a framework owning its own telemetry. The @nestjs/observe SDK sees your application in terms of controllers, providers, resolvers, and queue consumers because it lives inside the Nest request lifecycle. An external APM agent has to reconstruct that context by wrapping middleware and patching internals.
Route metadata is a good example. External agents parse URL patterns and try to match them to route handlers. The framework already knows the answer. Same with dependency injection context. Nest knows which provider handled a request. An external agent has to infer it.
This is the same direction we’re seeing across multiple ecosystems. Starlette is adding native OpenTelemetry spans to its router. Rails has had ActiveSupport::Notifications for years. Frameworks are starting to treat observability as a first-class feature rather than something bolted on by third parties.
Where It Falls Short
Framework-native observability is compelling, but it has real limitations that matter in production.
It’s NestJS-only. If your stack includes a Rails API, a Python ML service, and a NestJS gateway, @nestjs/observe sees one piece of that puzzle. You still need a separate tool for everything else, and now you’re correlating traces across two different systems.
It’s v1.0. The SDK just launched. It will have rough edges. Error handling, edge cases with custom transports, performance overhead under load. These things take time to shake out. Production observability tools have years of battle-testing behind them.
No cross-stack correlation out of the box. Distributed tracing works when every service in your architecture participates. A NestJS-specific SDK doesn’t automatically propagate trace context to your database queries, Redis calls, or downstream HTTP requests to non-Nest services.
Vendor lock-in at the framework level. If you ever migrate away from NestJS (or run a mixed framework setup, which is common), your observability data doesn’t come with you.
How Scout’s Approach Differs
We instrument at the runtime level across Node.js, Ruby, Python, PHP, and Elixir. When you install the Scout agent in a NestJS app, you get request tracing, N+1 query detection, error tracking, and memory bloat detection. You also get the same interface and the same trace format across every service in your stack, regardless of framework.
That tradeoff is real. We don’t have the same depth of framework-specific context that @nestjs/observe can provide. We can’t tell you which Nest provider handled a request as precisely as the framework itself can. But we give you a single view across your entire architecture, and we’ve been doing it in production for years.
For teams running NestJS exclusively, @nestjs/observe is worth evaluating. For teams running multiple frameworks or languages, a cross-stack APM tool is still the practical choice.
What This Means for Scout Users
Nothing changes today. Your Scout Node.js agent continues to work the same way with NestJS 12 as it did with v11. We’re testing against the v12 release and will update the agent if any compatibility issues surface.
We’re also watching how @nestjs/observe evolves. If it matures into a reliable telemetry source, there’s a path toward consuming its data alongside our own instrumentation for richer NestJS-specific context. That’s the same approach we’re considering for Starlette’s native OTel spans.
Try Scout Monitoring
We instrument NestJS, Express, Rails, Django, Laravel, and more. If you need application monitoring across your stack, Scout Monitoring is the fastest path to useful information without the bloat. Start your free trial.
For application monitoring with errors, logs, and traces, Scout Monitoring provides the fastest insights without the bloat.