DEV Community

Arpit Savaj
Arpit Savaj

Posted on

Rate Limiting in NestJS Using @nestjs/throttler

Image description

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:

  1. Why rate limiting is important

  2. How to configure global and route-specific limits

  3. How to override limits per route

  4. How to skip limits entirely

  5. 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 {}
Enter fullscreen mode Exit fullscreen mode

How It Works

If a user exceeds these limits, they get a 429 Too Many Requests error:

{
  "statusCode": 429,
  "message": "Too Many Requests"
}
Enter fullscreen mode Exit fullscreen mode

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";
  }
}
Enter fullscreen mode Exit fullscreen mode

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";
  }
}
Enter fullscreen mode Exit fullscreen mode

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):

  1. 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;

}
}
Enter fullscreen mode Exit fullscreen mode
  1. Use the Custom Guard in AppModule
providers: [
    {
    provide: APP_GUARD,
    useClass: EmailThrottlerGuard,
    },
    ],
Enter fullscreen mode Exit fullscreen mode

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)

Rate limiting helps you protect your API, scale responsibly, and ensure fair use. Whether you're building a small app or a large-scale platform, adding this layer is a smart move.
If you found this helpful and want more like this, drop a comment below or reach out to me!

Top comments (1)

Collapse
 
uday_solanki_9a558c59cbe0 profile image
Uday Solanki

Great post on rate limiting . Really helpful. Keep up the good work.