DEV Community

Maksym
Maksym

Posted on

Mastering Rate Limiting in NestJS with Throttler

In the world of web services and APIs, ensuring stability, security, and fair usage is paramount. One of the most effective techniques to achieve this is rate limiting. It prevents clients from making too many requests in a short period, protecting your application from abuse, denial-of-service (DoS) attacks, and resource exhaustion.

This article will guide you through implementing robust rate limiting in your NestJS application using the popular @nestjs/throttler package.


What is Rate Limiting?

Rate limiting is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain timeframe. For example, you might limit a user to 10 requests per second. If they exceed this limit, their subsequent requests are blocked, and they typically receive an HTTP 429 Too Many Requests status code.

Why is it crucial?

  • Security: Prevents brute-force attacks on login endpoints.
  • Reliability: Ensures your API remains available and responsive for all users by preventing a few clients from monopolizing server resources.
  • Cost Management: Controls the usage of paid third-party services your API might rely on.

Getting Started with @nestjs/throttler

The @nestjs/throttler package provides a flexible and easy-to-use way to add rate limiting to your NestJS projects.

1. Installation

First, install the necessary package in your project.

npm install @nestjs/throttler
Enter fullscreen mode Exit fullscreen mode

2. Basic Configuration

Next, you need to import and configure the ThrottlerModule in your root AppModule. This will set up a global rate-limiting rule for your entire application.

In src/app.module.ts:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';

@Module({
  imports: [
    ThrottlerModule.forRoot([{
      ttl: 60000, // Time-to-live: 60 seconds
      limit: 10,  // Allow 10 requests per 60 seconds per IP
    }]),
  ],
  controllers: [AppController],
  providers: [
    AppService,
    // Apply the ThrottlerGuard globally to all routes
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

In this configuration:

  • ThrottlerModule.forRoot() initializes the module with your desired rules.
  • ttl: The time window for the rate limit, specified in milliseconds. Here, 60000 ms is 60 seconds.
  • limit: The maximum number of requests allowed within the ttl window.
  • APP_GUARD: By providing ThrottlerGuard with this token, we are telling NestJS to use it as a global guard, automatically protecting every endpoint in the application.

Now, if a client makes more than 10 requests to any endpoint within a 60-second window, they will receive a 429 error response.


Advanced Customization

While a global rule is a great start, you often need more granular control. @nestjs/throttler offers powerful decorators to customize behavior for specific routes or controllers.

Overriding Rules with @Throttle()

You can override the global settings for a specific controller or even a single route using the @Throttle() decorator. This is useful for endpoints that need stricter or more lenient limits.

For example, a sensitive endpoint like login could have a much stricter limit.

import { Controller, Get, Post } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';

@Controller('auth')
export class AuthController {

  // Stricter limit for the login route
  @Throttle({ default: { limit: 3, ttl: 60000 } })
  @Post('login')
  login() {
    return { message: 'Logged in successfully' };
  }

  // This route will use the global limit (10 requests per 60s)
  @Get('profile')
  getProfile() {
    return { user: 'profile' };
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the /auth/login endpoint allows only 3 requests per minute, while /auth/profile falls back to the global rule of 10 requests per minute.

Skipping Rate Limiting with @SkipThrottle()

Sometimes, you may want to exclude certain endpoints from rate limiting altogether. The @SkipThrottle() decorator allows you to do just that.

import { Controller, Get } from '@nestjs/common';
import { SkipThrottle } from '@nestjs/throttler';

@Controller('status')
export class StatusController {

  // This endpoint is not rate-limited
  @SkipThrottle()
  @Get()
  getAppStatus() {
    return { status: 'ok' };
  }
}
Enter fullscreen mode Exit fullscreen mode

This is perfect for public, non-sensitive endpoints like a health check or status page.

Working with Proxies

By default, the throttler uses the direct IP address from the request. If your application is behind a reverse proxy (like Nginx or a load balancer), the IP address will always be that of the proxy. To fix this, you should trust the X-Forwarded-For header.

You can configure this in your main.ts file by telling your underlying platform (e.g., Express) to trust the proxy.

In src/main.ts:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // If using Express (default)
  app.getHttpAdapter().getInstance().set('trust proxy', 1);

  await app.listen(3000);
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Rate limiting is a non-negotiable feature for any production-grade API. The @nestjs/throttler package makes implementing it in NestJS incredibly simple yet highly customizable. By starting with a sensible global limit and then applying specific rules using decorators like @Throttle() and @SkipThrottle(), you can effectively protect your application from abuse, ensure high availability, and provide a fair and stable service to all your users. 🚀

Top comments (0)