NestJS 12.5.0, released on 12 November 2026, looks like a minor version but behaves like a hardening release. It adds new size and backlog limits to WebSockets, TCP, gRPC, Redis, RabbitMQ and multipart uploads, lowers several defaults, and turns some silent misconfigurations into startup errors. Most apps will be safer after upgrading, but a few will break if they relied on the old generous limits. This guide goes through the changes that matter and how to upgrade without surprises.

Upgrade all Nest packages together

The release notes say @nestjs/core and the platform packages should be updated together, because the adapters pass new route-matching options to core:

npm install @nestjs/[email protected] @nestjs/[email protected] \
  @nestjs/[email protected] \
  @nestjs/[email protected] @nestjs/[email protected]

# then check nothing is left behind
npm ls @nestjs/core @nestjs/common

Only install the packages you actually use (for example @nestjs/platform-fastify instead of Express).

1. WebSocket messages now capped at 1 MB

This is the change most likely to bite. WsAdapter now takes a maxPayload option, the largest message a client may send. The default is 1 MB, down from 100 MB. A larger message closes the connection with code 1009. A gateway’s own maxPayload takes precedence, and 0 lifts the limit.

If clients legitimately send large messages (for example, uploading images over a socket), raise the limit on that gateway only:

import { WebSocketGateway, SubscribeMessage, MessageBody } from '@nestjs/websockets';

@WebSocketGateway({ path: '/uploads', maxPayload: 8 * 1024 * 1024 }) // 8 MB
export class UploadGateway {
  @SubscribeMessage('chunk')
  handleChunk(@MessageBody() data: unknown) {
    // ...
  }
}

For Socket.IO, IoAdapter gains maxWaitingMessages: how many messages of one event a client may have waiting beyond those being handled (10,000 by default; Infinity lifts it). This stops one slow handler from letting a client queue unbounded work.

2. TCP microservices: new limits and a new default host

Three TCP changes to check:

  • maxBufferSize now defaults to 16 Mi characters (was 128 Mi) in both directions, and applies per packet, so a burst of small packets no longer closes the connection.
  • A new maxConcurrentMessages server option: 1,000 per connection by default; 0 lifts it.
  • An empty or unset host now listens on localhost only. If your TCP microservice runs in Docker or Kubernetes and other containers connect to it, you must now set the host explicitly.
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
    transport: Transport.TCP,
    options: {
      host: '0.0.0.0',   // required now to accept connections from other containers
      port: 3001,
    },
  });
  await app.listen();
}
bootstrap();

That localhost default is a sensible security change (nothing is exposed by accident), but it is a classic “works on my machine, fails in the cluster” upgrade issue. Check it first.

3. Other transport limits

  • gRPC: maxPendingResponses (1,000 by default; 0 disables). A streaming call whose client leaves responses unread fails with RESOURCE_EXHAUSTED.
  • Redis: maxWildcardChannelLength (1,024 characters) for requests matched through a wildcard subscription.
  • RabbitMQ: maxMessageSize (16 MiB) for messages and replies decoded as JSON.
  • Kafka: events can no longer request replies through reply headers, and a request whose reply topic is one the server itself handles is dropped.

4. Multipart uploads: 1,000 parts by default

For @nestjs/platform-express and @nestjs/platform-node, multipart requests now default to at most 1,000 parts. That also caps fields, files or maxCount above 1,000 unless you raise limits.parts:

import { Controller, Post, UploadedFiles, UseInterceptors } from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';

@Controller('gallery')
export class GalleryController {
  @Post()
  @UseInterceptors(FilesInterceptor('photos', 2000, { limits: { parts: 2500 } }))
  upload(@UploadedFiles() photos: Express.Multer.File[]) {
    return { count: photos.length };
  }
}

Two related rules changed. An upload option a route sets to undefined or null now keeps the module-level value (use Infinity to lift a limit). And a limit given as NaN, which is what Number(process.env.UNSET) produces, now falls back to the default instead of silently disabling the limit. That second fix closes a sneaky hole where a missing environment variable turned protection off.

On Fastify, multipart limits.jsonFieldsSize (1 MiB by default) bounds fields sent as application/json.

5. Errors you will now see at startup

Several misconfigurations that used to fail quietly now stop the app when it boots:

  • A guard, interceptor or pipe that cannot run (for example, an abstract class used as a token, or a class given as the useValue of APP_GUARD).
  • An invalid forRoutes() or exclude() middleware entry.
  • Gateways sharing a server with conflicting hosts or a verifyClient the server would not run.

There are also new warnings for exception filters bound to middleware (they never applied) and for a ValidationPipe that receives no parameter types. If your app fails to start after upgrading, read the error: it is usually pointing at a guard or middleware that never worked.

6. Behavior changes worth testing

  • ClassSerializerInterceptor now serializes each object a streamed HTTP response yields, so @Exclude() and groups apply to every NDJSON line. Good for privacy, but check streaming endpoints.
  • @Sse() writes a : comment every 5 seconds while a client’s socket is paused, so disconnected clients are noticed.
  • A SyntaxError or URIError without a 400 status (for example from middleware) now returns a logged 500 rather than a 400 that leaked its message.
  • The exclude predicate of enableCsrfProtection() excludes a request only when it returns exactly true.
  • An @Optional() dependency that exists but fails to build at request time now fails its consumer.

A safe upgrade checklist

  1. Upgrade every @nestjs/* package to 12.5.0 at once.
  2. Set host: '0.0.0.0' on TCP microservices that are reached from other machines or containers.
  3. Search your gateways for large messages and set maxPayload where needed.
  4. Check upload routes that accept many files or fields.
  5. Boot the app and fix any new startup errors.
  6. Run end-to-end tests for streaming, SSE and WebSocket flows.

Takeaway

NestJS 12.5 swaps “unlimited unless you remember” for “bounded unless you opt out”, which is the right default for internet-facing Node.js services. The upgrade is quick for most apps, but check the TCP localhost default, the 1 MB WebSocket cap and the multipart part limit before you deploy.