Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
Node.jsDevelopmentJavaScriptIndustry update

NestJS 12.1: what changed and how to upgrade safely

NestJS 12.1.0 to 12.1.2 add Fastify uploads, cookies and CSRF protection and fix logging and reconnection. Behaviour changes and tested upgrade steps.

Cloudsoft Zone

Updated 11 min read

Timeline of NestJS releases from 27 August to 30 September 2026: 12.0.0 to 12.1.2 on the 12.x line and 11.2.4 to 11.2.7 on the 11.x line
Image: Cloudsoft Zone
In this article

NestJS 12.1 is the first feature release of the 12.x line, and it is worth taking now: it adds file-upload interceptors for Fastify, adapter-agnostic cookies, built-in CSRF protection and security headers, and the two patch releases that followed fix real problems in JSON logging and microservice reconnection. This guide lists what changed in 12.1.0, 12.1.1 and 12.1.2, the behaviour changes that can surprise a running application, and the upgrade steps from 12.0 and from 11.x that we ran end to end in a scratch project.

Tested with: Node.js 24.19.0, @nestjs/core 12.1.2, @nestjs/cli 12.0.8, @nestjs/schematics 12.0.6, TypeScript 6.0.3 (upgrade run) and 5.9.3 (feature checks), Fastify 5.12.5, on 30 September and 1 October 2026.

The releases at a glance

The 12.x line moved quickly after 12.0.0 shipped on 27 August 2026. Three releases make up 12.1 so far, and the 11.x line received security and maintenance releases in the same weeks.

VersionDateWhat matters
12.1.023 Sep 2026Fastify upload interceptors, built-in cookies, CSRF protection and security headers, several global prefixes, transient middleware fix
12.1.128 Sep 2026Seven ConsoleLogger fixes, reconnection fixes for TCP, NATS, Kafka, RabbitMQ, MQTT and Redis clients, circular dependency detection
12.1.230 Sep 2026Media type versioning reads the version from any Accept parameter, more listener and pending-request fixes across transports
11.2.6 / 11.2.723 / 30 Sep 2026multer 2.4.0 for CVE-2026-88932 in platform-express, then WebSocket fixes

The 12.1.0 and 12.1.1 notes are on GitHub. At the time of writing 12.1.2 has a git tag and an npm release but no GitHub release page, so its contents below come from the commit comparison between the two tags.

What 12.1.0 added

File uploads on Fastify

Until 12.1, FileInterceptor and its siblings were Express-only because they wrap multer. PR #17835 adds the same interceptors to @nestjs/platform-fastify/multipart, backed by @fastify/multipart (an optional peer dependency, ^10.1.0). ParseFilePipe and the built-in validators work unchanged, and there is a Fastify-only FileStreamInterceptor that hands your handler a stream instead of a buffer. Two defaults differ from multer and will bite you: the maximum file size falls back to Fastify's bodyLimit (1 MiB), and the number of parts is capped at 1000. We cover limits, validation and streaming to object storage in a separate tutorial.

Cookies, CSRF protection and security headers without extra packages

PR #17834 adds @Cookies() and @SignedCookies() parameter decorators, setCookie() and clearCookie() on the HTTP adapter, and a cookies: { secret } application option that accepts several secrets for rotation. PR #17836 adds app.useSecurityHeaders() (helmet 8 defaults, including a Content Security Policy) and app.enableCsrfProtection(), which follows Go 1.25's CrossOriginProtection: safe methods pass, Sec-Fetch-Site: same-origin passes, requests with neither Sec-Fetch-Site nor Origin pass (non-browser clients), and anything else must have an Origin that matches the host. We ran this on Fastify:

TypeScript
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter(), {
  cookies: { secret: [process.env.COOKIE_SECRET!, process.env.COOKIE_SECRET_PREVIOUS!] },
});
app.useSecurityHeaders();          // must be called before app.init() / app.listen()
app.enableCsrfProtection({
  trustedOrigins: ['https://admin.example.com'],
  exclude: [{ path: 'webhooks/stripe', method: RequestMethod.POST }],
});

@Controller()
class AuthController {
  constructor(private readonly host: HttpAdapterHost) {}

  @Post('login')
  login(@Res({ passthrough: true }) res: unknown) {
    this.host.httpAdapter.setCookie!(res, 'uid', '42', {
      signed: true, httpOnly: true, secure: true, sameSite: 'lax', maxAge: 3600, // seconds
    });
    return { ok: true };
  }

  @Get('me')
  me(@SignedCookies('uid') uid?: string) {
    return { uid: uid ?? null }; // undefined when missing or forged
  }
}

Results from our run: the login response set uid=s%3A42.<signature>; Path=/; Max-Age=3600; HttpOnly; Secure; SameSite=Lax and a content-security-policy header; a POST with Sec-Fetch-Site: cross-site and a foreign Origin returned 403 with "Cross-origin request detected from Sec-Fetch-Site header"; a POST with neither header (as curl or a server-to-server call sends) returned 201; a forged signed cookie came back as null. Note the maxAge unit: it is seconds here, while Express's res.cookie() takes milliseconds, so do not paste old option objects across.

The CSRF check is origin-based, not token-based. It protects cookie-authenticated browser sessions; it does nothing for APIs authenticated with bearer tokens, which are not sent automatically by browsers anyway. If you already run helmet or @fastify/helmet, pick one source of headers rather than layering both.

Smaller 12.1.0 changes

  • app.setGlobalPrefix(['api', 'v1']) registers every route under each prefix (PR #17713). @nestjs/swagger still reads only the first prefix, so documentation lists /api/... only.
  • A TRANSIENT-scoped middleware no longer stops the middleware listed after it in the same apply() call from being registered (PR #17846). More on this below, because it changes behaviour.
  • The Fastify adapter honours Nest's forceCloseConnections, route handler names are preserved, and the Redis client retries after a failed first connect.

What 12.1.1 and 12.1.2 fixed

PR #17893 fixes seven ConsoleLogger bugs. The two you are most likely to have hit: in JSON mode a circular object made log() throw, and an error passed after the message produced two records, the second holding a multi-line util.inspect dump. We ran the same script against 12.0.4 and 12.1.2:

Terminal output comparing NestJS ConsoleLogger JSON mode: 12.0.4 prints two records and crashes on a circular object, 12.1.2 prints one record with a structured error
The same logging script on 12.0.4 and 12.1.2: one structured record instead of two, and no crash on circular data.

Since 12.1.1 the first Error becomes a structured error field with name, message, stack and a recursive cause, circular values become "[Circular]", fatal() writes to stderr, runtime level changes reach existing Logger instances, and bufferLogs output is flushed by app.init() as well as listen(), which matters for serverless handlers and e2e tests that never listen.

On the microservices side, 12.0.4 to 12.1.2 contain a long run of reconnection fixes: the Kafka, RabbitMQ, MQTT and Redis clients now retry after a failed first connect instead of staying broken; the TCP client keeps its event listeners across reconnects and ignores events from a replaced socket; NATS and Kafka keep a newer connection when a stale one fails; pending NATS and RabbitMQ requests fail promptly when the client closes or gives up instead of hanging. If you run request-response over any broker, these alone justify the upgrade.

12.1.2 also changes versioning slightly: media type versioning now reads the version from any parameter of the Accept header, and error replies keep +json content types. ParseUUIDPipe accepts versions 1, 2, 6 and 8 and an 'all' option.

Behaviour changes to check before you deploy

ChangeWho is affectedWhat to do
Middleware after a transient middleware now runsApps that list a Scope.TRANSIENT middleware (or a subclass inheriting that scope) first in apply(A, B, C)Middleware that was silently skipped, such as an auth or tenant check, will start rejecting requests. Test those routes.
JSON log shapeAnyone parsing json: true logsErrors arrive in one record with an error object; update log queries and alerts that matched the old second record.
Circular dependency detection across more than two providersApps with an A → B → C → A cycle that happened to resolveBoot now fails with a clear error; break the cycle or use forwardRef().
ParseArrayPipe with items: NumberQuery strings like ?ids=1,xNon-numeric items are now rejected with 400.
Value provider overrides in testsTests using overrideProvider().useClass() on a value providerThe override is now honoured; tests that passed by accident may change.

The transient middleware change is the one most likely to hurt in production, because it turns code that never ran into code that runs. If your API has grown middleware no one has looked at for a while, a short review of what each one does before upgrading is cheap insurance; this is the kind of check our API development and integration team does as part of a NestJS upgrade.

Upgrading from 12.0.x

This is a minor upgrade with no breaking API changes. Keep every @nestjs/* package on the same version, because the packages depend on each other's internals:

Shell
npm install @nestjs/common@^12.1.2 @nestjs/core@^12.1.2 @nestjs/platform-fastify@^12.1.2 \
  @nestjs/microservices@^12.1.2 @nestjs/testing@^12.1.2
npm ls @nestjs/core @nestjs/common   # one version each, no duplicates
# Only if you will use the Fastify upload interceptors:
npm install @fastify/multipart@^10.1.0

Then run your test suite, check one JSON log line from staging against your log pipeline, and watch reconnect and error metrics for the first day.

Diagram of two upgrade paths to NestJS 12.1.2: from 12.0.x by aligning package versions and checking logs and middleware, from 11.x via Node.js, CLI 12, nest upgrade and tsconfig changes
The upgrade path depends on where you start; 11.x users who cannot move yet should take 11.2.7.

Upgrading from 11.x: what we hit and how we fixed it

The official migration guide and the 12.0.0 notes recommend nest upgrade, which ships with CLI 12. NestJS 12 requires Node.js 20.19+ or 22.12+ (not the 21.x line), because the packages are now ESM and rely on require(esm). We took a small NestJS 11.2 API on Fastify with @nestjs/config and Joi validation and upgraded it. Four things failed before it worked:

  1. Invalid command: upgrade --dry-run. A globally installed CLI 12 hands over to the project's local @nestjs/cli 11, which has no upgrade command. Fix: npm i -D @nestjs/cli@12 first.
  2. Collection "@nestjs/schematics" cannot be resolved. The upgrade runs as a schematic. Our project had no local @nestjs/schematics (projects generated by nest new do). Fix: install it.
  3. ERESOLVE ... peer typescript@">=6.0.0" from @nestjs/schematics@12.0.6. Schematics 12 needs TypeScript 6 or later. Fix: npm i -D typescript@6. The NestJS runtime packages themselves compiled fine with TypeScript 5.9 in our other scratch projects; the TypeScript 6 requirement comes from the schematics package.
  4. TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set. TypeScript 6 now insists on rootDir when outDir is set. Fix: add "rootDir": "./src".
Shell
node -v                                   # must be >= 20.19 or >= 22.12
npm i -D @nestjs/cli@12 @nestjs/schematics@12 typescript@6
npx nest upgrade --dry-run --no-observe   # read the report; nothing is written
npx nest upgrade --no-observe             # applies changes and runs npm install
Terminal output of nest upgrade --dry-run on a NestJS 11 project listing package updates, a config migration, a logger warning and a required tsconfig change
Our dry run on a NestJS 11.2 project (abridged): it edits package.json and code, and tells you what it will not fix.

The dry run reported exactly what it would change: every @nestjs/* package (including @nestjs/config, now versioned 12) moved to ^12.0.0, Joi from 17 to 18 because @nestjs/config 12 validates through Standard Schema, and our validationOptions moved under validationOptions.libraryOptions. It flagged a logger.log('listing orders', { tenant }) call because objects after the message are now structured params, and it told us, but did not fix, that "module": "commonjs" with legacy resolution cannot resolve the ESM-only packages. The tsconfig that built and ran:

JSON
{
  "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "resolvePackageJsonExports": true,
    "target": "ES2023",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true
  }
}

Without "type": "module" in package.json the emitted code stays CommonJS, and nest build plus a request to the running app both succeeded. Because the ranges were ^12.0.0, the install resolved @nestjs/core 12.1.2 directly, so 11.x users land on 12.1 in one step. The upgrade does not move you to ESM, Vitest or oxlint; those are defaults only for new projects.

If you are staying on 11.x for now

Take 11.2.7. The 11.2.6 release updated @nestjs/platform-express to multer 2.4.0 for CVE-2026-88932 (GHSA-3pph-fpjx-jg34, medium: denial of service through orphaned disk writes on aborted uploads, affecting multer 2.2.0 to 2.3.x), and mapped multer errors by code so an unexpected field returns 400 again rather than 500. The 11.2.4 and 11.2.5 releases fixed a Fastify middleware bypass via absolute-form request targets and hardened the TCP transport against a hostile peer. None of the 12.1 features are backported.

Who should upgrade now

  • Microservices on any broker: the reconnection fixes remove failure modes where a client stayed broken after the broker restarted.
  • Anyone using JSON logs: a circular object in a log call can no longer crash a request path.
  • Fastify users who needed uploads: you can drop hand-rolled @fastify/multipart code.
  • Express users who accept uploads on 11.x: at minimum take 11.2.7 for the multer fix.

Common mistakes

  • Upgrading @nestjs/core but leaving @nestjs/platform-fastify or @nestjs/microservices on an older minor.
  • Running nest upgrade without --dry-run first, then losing track of what it changed.
  • Calling useSecurityHeaders() or enableCsrfProtection() after app.init(); both throw.
  • Assuming the CSRF check protects token-authenticated APIs or webhooks; exclude webhook routes that browsers never call.
  • Copying Express cookie options with millisecond maxAge values into the new seconds-based API.

When not to upgrade yet

Hold back if you are still on Node.js 18 or the 21.x line, if a third-party Nest library you depend on has not published a 12-compatible release, or if you are mid-release on a product with no time to test the middleware change. In those cases pin 11.2.7, plan the Node.js upgrade first (our NestJS with Fastify production guide covers a 12.x baseline), and schedule the move to 12.1 as a separate change.

Planning a NestJS 11 to 12 upgrade, or adding uploads and CSRF protection to an existing API? Cloudsoft Zone builds and upgrades NestJS APIs, with the dependency review, test run and staged rollout done for you.

Frequently asked questions

  • Is NestJS 12.1 a breaking upgrade from 12.0?

    No API was removed, but some fixes change behaviour: middleware listed after a transient middleware now runs, JSON logs put errors in one record, and deeper circular dependencies now fail at boot. Test those areas before deploying.

  • Does nest upgrade take a NestJS 11 project straight to 12.1?

    In our run it did. It wrote ^12.0.0 ranges and the install resolved 12.1.2. You still have to change tsconfig to nodenext module resolution yourself, and the CLI and schematics 12 must be installed locally first.

  • Do I need TypeScript 6 for NestJS 12?

    @nestjs/schematics 12 declares a TypeScript 6 peer dependency, so the CLI tooling needs it. The runtime packages compiled with TypeScript 5.9 in our tests. If you move to TypeScript 6, set rootDir explicitly.

  • Can I use the new upload interceptors with Express?

    No. The interceptors in @nestjs/platform-fastify/multipart only work with the Fastify adapter. Express applications keep using the multer-based interceptors from @nestjs/platform-express.

  • Which 11.x version should I run if I cannot upgrade?

    11.2.7, released on 30 September 2026. It includes the multer 2.4.0 update for CVE-2026-88932 from 11.2.6 and the earlier Fastify and TCP fixes.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Release v12.1.0 — NestJS (GitHub), 2026-09-23
  2. Release v12.1.1 — NestJS (GitHub), 2026-09-28
  3. Comparing v12.1.1...v12.1.2 — NestJS (GitHub), 2026-09-30
  4. Release v12.0.0 — NestJS (GitHub), 2026-08-27
  5. fix(common): logger bugs in json output, level changes, fatal and buffered logs (#17893) — NestJS (GitHub), 2026-09-28
  6. feat(common,core,fastify): built-in CSRF protection and security headers (#17836) — NestJS (GitHub), 2026-09-22
  7. Migration guide — NestJS documentation
  8. multer vulnerable to Denial of Service via orphaned disk writes on aborted uploads (GHSA-3pph-fpjx-jg34) — GitHub Advisory Database, 2026-09-28
  9. Release v11.2.6 — NestJS (GitHub), 2026-09-23

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.