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.
Updated 11 min read

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.
| Version | Date | What matters |
|---|---|---|
| 12.1.0 | 23 Sep 2026 | Fastify upload interceptors, built-in cookies, CSRF protection and security headers, several global prefixes, transient middleware fix |
| 12.1.1 | 28 Sep 2026 | Seven ConsoleLogger fixes, reconnection fixes for TCP, NATS, Kafka, RabbitMQ, MQTT and Redis clients, circular dependency detection |
| 12.1.2 | 30 Sep 2026 | Media type versioning reads the version from any Accept parameter, more listener and pending-request fixes across transports |
| 11.2.6 / 11.2.7 | 23 / 30 Sep 2026 | multer 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:
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/swaggerstill reads only the first prefix, so documentation lists/api/...only.- A
TRANSIENT-scoped middleware no longer stops the middleware listed after it in the sameapply()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:

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
| Change | Who is affected | What to do |
|---|---|---|
| Middleware after a transient middleware now runs | Apps 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 shape | Anyone parsing json: true logs | Errors 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 providers | Apps with an A → B → C → A cycle that happened to resolve | Boot now fails with a clear error; break the cycle or use forwardRef(). |
ParseArrayPipe with items: Number | Query strings like ?ids=1,x | Non-numeric items are now rejected with 400. |
| Value provider overrides in tests | Tests using overrideProvider().useClass() on a value provider | The 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:
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.0Then 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.

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:
Invalid command: upgrade --dry-run. A globally installed CLI 12 hands over to the project's local@nestjs/cli11, which has noupgradecommand. Fix:npm i -D @nestjs/cli@12first.Collection "@nestjs/schematics" cannot be resolved. The upgrade runs as a schematic. Our project had no local@nestjs/schematics(projects generated bynest newdo). Fix: install it.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.TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set. TypeScript 6 now insists onrootDirwhenoutDiris set. Fix: add"rootDir": "./src".
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
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:
{
"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/multipartcode. - Express users who accept uploads on 11.x: at minimum take 11.2.7 for the multer fix.
Common mistakes
- Upgrading
@nestjs/corebut leaving@nestjs/platform-fastifyor@nestjs/microserviceson an older minor. - Running
nest upgradewithout--dry-runfirst, then losing track of what it changed. - Calling
useSecurityHeaders()orenableCsrfProtection()afterapp.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
maxAgevalues 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.0ranges and the install resolved 12.1.2. You still have to change tsconfig tonodenextmodule resolution yourself, and the CLI and schematics 12 must be installed locally first.Do I need TypeScript 6 for NestJS 12?
@nestjs/schematics12 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, setrootDirexplicitly.Can I use the new upload interceptors with Express?
No. The interceptors in
@nestjs/platform-fastify/multipartonly 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.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- Release v12.1.0 — NestJS (GitHub), 2026-09-23
- Release v12.1.1 — NestJS (GitHub), 2026-09-28
- Comparing v12.1.1...v12.1.2 — NestJS (GitHub), 2026-09-30
- Release v12.0.0 — NestJS (GitHub), 2026-08-27
- fix(common): logger bugs in json output, level changes, fatal and buffered logs (#17893) — NestJS (GitHub), 2026-09-28
- feat(common,core,fastify): built-in CSRF protection and security headers (#17836) — NestJS (GitHub), 2026-09-22
- Migration guide — NestJS documentation
- multer vulnerable to Denial of Service via orphaned disk writes on aborted uploads (GHSA-3pph-fpjx-jg34) — GitHub Advisory Database, 2026-09-28
- 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.
More from Cloudsoft Zone

ComparisonNode.js
Express 5 vs Fastify 5: which Node.js framework to pick in 2026
Express 5 and Fastify 5 compared on support status, validation, error handling, upgrades and performance, with the same API built and tested in both.
By Cloudsoft Zone

TutorialNode.js
How to test a Node.js API with the built-in test runner (node:test)
Write, run, mock and measure coverage for a small Node.js HTTP API using only node:test and node:assert. Covers port-0 servers, fetch(), spies, t.mock.method(), fake dates and coverage thresholds.
By Cloudsoft Zone

TutorialNode.js
Node.js streams in practice: pipeline(), backpressure and async generators
Process files of any size in Node.js with flat memory use. A hands-on guide to stream/promises pipeline(), backpressure, async generator transforms, AbortSignal timeouts and streaming downloads over HTTP.
By Cloudsoft Zone
