NestJS Rate Limiting
Rate limiting caps the number of requests a client can make to your API within a time window. Without it, a single bad actor can flood your server with thousands of requests per second — crashing it, exhausting your database connections, or running up your cloud bill. Rate limiting protects your infrastructure, ensures fair usage across all clients, and is a standard security requirement for any public-facing API.
How Rate Limiting Works
Client sends requests to GET /products: Request 1 → allowed (1/10 used) Request 2 → allowed (2/10 used) ... Request 10 → allowed (10/10 used) Request 11 → BLOCKED (429 Too Many Requests) "You've made 10 requests in 60 seconds. Try again in 45 seconds." After 60 seconds: counter resets → 10 more requests allowed
Installing the Rate Limiter
npm install @nestjs/throttler
Global Rate Limiting Setup
// app.module.ts
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';
@Module({
imports: [
ThrottlerModule.forRoot([
{
name: 'short',
ttl: 1000, // 1 second window
limit: 3, // 3 requests per second
},
{
name: 'long',
ttl: 60000, // 60 second window
limit: 100, // 100 requests per minute
},
]),
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard, // applies to every route globally
},
],
})
export class AppModule {}
Multiple Throttle Tiers
Time Window | Limit | Purpose -------------|-------|-------------------------------- 1 second | 3 | Prevent rapid-fire burst requests 60 seconds | 100 | Enforce overall usage cap per minute 1 hour | 1000 | Daily API quota enforcement Client blocked when any limit is hit first.
Skipping Rate Limiting on Specific Routes
import { SkipThrottle } from '@nestjs/throttler';
@Controller('health')
@SkipThrottle() // health checks must never be rate-limited
export class HealthController {
@Get()
check() { return 'OK'; }
}
Custom Limits Per Route
import { Throttle } from '@nestjs/throttler';
@Controller('auth')
export class AuthController {
@Post('login')
@Throttle({ short: { ttl: 60000, limit: 5 } }) // 5 login attempts per minute
login(@Body() dto: LoginDto) {
return this.authService.login(dto);
}
@Post('register')
@Throttle({ short: { ttl: 3600000, limit: 3 } }) // 3 registrations per hour
register(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
}
Authentication endpoints need tighter limits than regular API endpoints. An attacker trying to brute-force passwords sends thousands of login attempts per minute. A limit of 5 per minute makes brute force attacks impractical while not inconveniencing legitimate users who typically log in once.
Rate Limit Response Headers
The throttler automatically adds helpful headers to every response:
X-RateLimit-Limit: 100 X-RateLimit-Remaining: 87 X-RateLimit-Reset: 1705000060 Retry-After: 45 (only when blocked)
Clients read these headers to understand their current usage and when to retry blocked requests. Well-designed API clients back off automatically when they receive a 429 response and use Retry-After to know when to resume.
Distributed Rate Limiting with Redis
The default throttler stores counters in memory. This works fine for a single server, but fails when you run multiple application instances — each instance has its own counter, so a client can exceed the limit by spreading requests across instances. Use Redis as a shared store:
npm install @nestjs/throttler-storage-redis ioredis
ThrottlerModule.forRoot({
throttlers: [{ ttl: 60000, limit: 100 }],
storage: new ThrottlerStorageRedisService(
new Redis({ host: 'localhost', port: 6379 })
),
}),
IP-Based vs User-Based Throttling
Default (IP-based):
Counter keyed by client IP address
Problem: multiple users behind NAT share one IP
Custom (user-based for authenticated routes):
Counter keyed by user ID from JWT
More precise — each user gets their own quota
// Custom throttler that uses user ID when authenticated
@Injectable()
export class UserThrottlerGuard extends ThrottlerGuard {
protected async getTracker(req: Request): Promise<string> {
return req['user']?.id?.toString() || req.ip;
}
}
Rate limiting is one of the fastest security improvements you can make to a NestJS API. The throttler package requires minimal configuration, applies globally in two lines of code, and immediately protects every route from abuse — while remaining flexible enough to apply different limits to different endpoint types based on their sensitivity and expected usage patterns.
