NestJS — Enterprise Node.js

Rate Limiting, Security & Performance

16 min Lesson 42 of 48

Rate Limiting, Security & Performance

A production NestJS application must defend itself against abuse, expose the right HTTP security headers, and be tuned for throughput. This lesson covers four complementary concerns: rate limiting with @nestjs/throttler, HTTP security headers with Helmet, CORS configuration, and performance tuning — including the Fastify adapter, response compression, and payload size limits.

Rate Limiting with @nestjs/throttler

The throttler module blocks clients that make too many requests in a given time window, protecting your API from brute-force attacks and runaway consumers.

npm install @nestjs/throttler

Register the module globally and apply the guard:

// app.module.ts import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; import { APP_GUARD } from '@nestjs/core'; @Module({ imports: [ ThrottlerModule.forRoot([ { ttl: 60_000, limit: 100 }, // 100 requests per 60 s per IP ]), ], providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }], }) export class AppModule {}

Override per-route with the @Throttle() decorator, or exempt internal routes with @SkipThrottle():

import { Throttle, SkipThrottle } from '@nestjs/throttler'; @Controller('auth') export class AuthController { @Throttle([{ ttl: 60_000, limit: 5 }]) // stricter: 5 login attempts/min @Post('login') login(@Body() dto: LoginDto) { /* ... */ } @SkipThrottle() @Get('health') health() { return { ok: true }; } }
Storage backends. By default throttler stores counters in memory — fine for a single instance. For a multi-instance deployment (load-balanced), switch to the Redis storage adapter (ThrottlerStorageRedisService from @nestjs/throttler-storage-redis) so all pods share the same counters.

HTTP Security Headers with Helmet

Helmet sets a collection of well-known HTTP response headers that harden your app against common browser-based attacks (XSS, clickjacking, MIME sniffing, etc.).

npm install helmet
// main.ts (Express adapter — default) import { NestFactory } from '@nestjs/core'; import helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(helmet()); // sets X-Frame-Options, CSP, HSTS, and more await app.listen(3000); } bootstrap();
Customise CSP carefully. helmet()'s default Content-Security-Policy may break inline scripts or external CDN assets. Pass an options object to helmet({ contentSecurityPolicy: { directives: { ... } } }) to tailor it, or disable only the CSP rule while keeping the rest.

CORS Configuration

CORS must be enabled for browser clients to call your API from a different origin. NestJS wraps the cors package:

// main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors({ origin: ['https://app.example.com', 'https://www.example.com'], methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], credentials: true, // allow cookies / Authorization header maxAge: 86_400, // preflight cache: 24 h }); await app.listen(3000); } bootstrap();
Never use origin: '*' with credentials: true. Browsers reject this combination. Either specify exact origins or use a dynamic validator function that checks against an allow-list and returns the origin or false.

Performance: Fastify Adapter

NestJS defaults to Express. Swapping in the Fastify adapter typically doubles raw throughput because Fastify has a faster HTTP parser and a more efficient routing engine.

npm install @nestjs/platform-fastify
// main.ts — Fastify adapter import { NestFactory } from '@nestjs/core'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; async function bootstrap() { const app = await NestFactory.create<NestFastifyApplication>( AppModule, new FastifyAdapter({ logger: true }), ); await app.listen(3000, '0.0.0.0'); } bootstrap();
Trade-offs. Most NestJS code is adapter-agnostic, but some Express-specific middleware (app.use(someExpressMiddleware)) does not work with Fastify. Check your middleware list before switching.

Compression & Payload Limits

Enabling gzip compression reduces response sizes significantly for JSON-heavy APIs. Use the compression package (Express) or Fastify's built-in plugin:

// Express adapter npm install compression npm install -D @types/compression // main.ts import * as compression from 'compression'; app.use(compression());

Limit incoming request body size to prevent memory exhaustion attacks. With Express, set it when registering body parsers:

// main.ts — body size limit (Express) const app = await NestFactory.create(AppModule, { bodyParser: false, // disable default to customise }); import * as express from 'express'; app.use(express.json({ limit: '1mb' })); app.use(express.urlencoded({ extended: true, limit: '1mb' }));

Summary

Secure, performant NestJS applications combine @nestjs/throttler for rate limiting (global guard + per-route overrides), Helmet for security headers, CORS configured with an explicit allow-list and credentials: true only when needed, the optional Fastify adapter for higher throughput, compression to shrink responses, and body-size limits to prevent abuse. These are not optional extras — they are baseline requirements for any internet-facing service.