What is Rate Limiting?
Rate limiting is a technique used to control the number of requests a user or system can make to a server in a given time period.
Think of it like this:
You can’t drink an entire bottle of water in one gulp (at least you shouldn’t 😅). You sip it slowly. Similarly, APIs and servers also want users to “sip” data—request by request, not flood the server with 1000 requests per second.
It’s basically saying:
“Hey, you can only make 100 requests per 15 minutes. Chill.”
In this blog, I’ll walk you through how to implement rate limiting in a NestJS application using the official @nestjs/throttler package.
We’ll cover:
Why rate limiting is important
How to configure global and route-specific limits
How to override limits per route
How to skip limits entirely
How to apply rate limiting based on users (like email)
Let’s get started! 🚀
Why Do We Need Rate Limiting?
Without rate limiting, bots or even regular users can spam your API with a flood of requests. This can lead to:
Increased server load
Slower response times
Downtime or crashes
Rate limiting solves this by capping how many requests are allowed in a given time period.
Installing Throttler
First, install the throttler package:
npm install @nestjs/throttler
Setting Up Global Rate Limiting
In your AppModule, you can define multiple throttling rules using different names.
import { Module } from "@nestjs/common";
import { APP_GUARD } from "@nestjs/core";
import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
@Module({
imports: [
ThrottlerModule.forRoot({
throttlers: [
{
name: "short",
ttl: 10000, // 10 seconds
limit: 3, // 3 requests per 10 sec
},
{
name: "long",
ttl: 86400000, // 24 hours
limit: 9, // 9 requests per day
},
],
}),
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
],
})
export class AppModule {}
How It Works
If a user exceeds these limits, they get a 429 Too Many Requests error:
{
"statusCode": 429,
"message": "Too Many Requests"
}
Override Global Limits per Route
Use the @Throttle() decorator to set different limits for individual routes:
import { Controller, Get } from "@nestjs/common";
import { Throttle } from "@nestjs/throttler";
@Controller("products")
export class ProductController {
@Throttle({ default: { limit: 5, ttl: 30000 } }) // 5 requests per 30 seconds
@Get()
getProducts() {
return "products list";
}
}
This overrides the global limit for this route only.
Skip Rate Limiting for Specific Controllers or Routes
You can exclude an entire controller or just a specific route from throttling using @SkipThrottle().
Skip for Entire Controller
import { Controller, Get } from "@nestjs/common";
import { SkipThrottle } from "@nestjs/throttler";
@SkipThrottle()
@Controller("users")
export class UsersController {
@Get()
findAll() {
return "user list";
}
}
//Apply Skip Inside the Same Controller
@Controller("products")
export class ProductController {
@SkipThrottle({ default: false }) // Explicitly apply rate limit
@Get("image")
getImage() {
return "product image";
}
@SkipThrottle() // Skip rate limit for this route
@Get("title")
getTitle() {
return "product title";
}
}
Custom Rate Limiting Based on Email Instead of IP
By default, throttler uses the IP address to track requests. You can customize this by creating a custom guard that tracks based on the user’s email (if authenticated):
- Create a Custom Guard
import {Injectable,CanActivate,ExecutionContext} from '@nestjs/common';
import {ThrottlerGuard,ThrottlerStorage,ThrottlerModuleOptions,} from '@nestjs/throttler';
import { JwtService } from '@nestjs/jwt';
import { AuthJwtConfig } from 'src/configs/auth-jwt.config';
import { Reflector, ModuleRef } from '@nestjs/core';
import { UsersApiService } from 'src/users/users-api.service';
import { UserSelectTypeQuery } from 'src/users/filters/user.filter';
@Injectable()
export class EmailThrottlerGuard extends ThrottlerGuard implements CanActivate{
private jwtService: JwtService;
private authJwtConfig: AuthJwtConfig;
private userData:UsersApiService;
constructor(
options: ThrottlerModuleOptions,
storageService: ThrottlerStorage,
reflector: Reflector,
moduleRef: ModuleRef,
) {
super(options, storageService, reflector);
this.jwtService = moduleRef.get(JwtService, { strict: false });
this.authJwtConfig = moduleRef.get(AuthJwtConfig, { strict: false });
this.userData=moduleRef.get(UsersApiService,{strict:false})
}
protected async getTracker(req: Record<string, any>): Promise<string> {
// Check if it is login req and the request body contains a `username`.
// If available, use it to generate a unique key for rate limiting.
// This ensures that rate limiting is applied per user rather than per IP.
// if not login req then take token from req headers and decrypt token and find user id from token after that
// find user email from database and set as a key for rate limiting
let key = 'anonymous';
if (req.url === '/auth/login') {
const username = req.body?.username;
if (username) {
key = `rate-limit-${username}`;
}
return key;
}
try {
const authHeader = req.headers?.authorization;
const token = authHeader?.split(' ')[1];
if (token) {
const decoded = this.jwtService.verify(token, {
secret: this.authJwtConfig.secret,
});
if (decoded?.sub) {
const parmaData=req.params
parmaData.selectType="detail"
const user = await this.userData.findOne(decoded.sub,new UserSelectTypeQuery())
const userEmail = user?.email
key = `rate-limit-${userEmail}`;
}
}
else{
if (req.body?.username) {
key = `rate-limit-${req.body.username}`;
}
}
} catch (err) {
console.warn('JWT verification failed:', err.message);
}
return key;
}
}
- Use the Custom Guard in AppModule
providers: [
{
provide: APP_GUARD,
useClass: EmailThrottlerGuard,
},
],
This allows you to limit each logged-in user separately, instead of by IP address.
Multiple Throttle Profiles: When & Why?
You can define multiple throttle profiles like "short" for spam-sensitive routes and "long" for daily caps. This allows more fine-grained control across different routes and use cases.
For example:
/login → use short burst limits
/daily-report → use long-term limits
Conclusion
By using @nestjs/throttler, you can:
Easily apply global rate limits
Override them per route with @Throttle()
Skip rate limits for certain routes using @SkipThrottle()
Customize user tracking via email instead of IP
Define multiple rate limit strategies (short vs long windows)