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
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 {}
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 thettl
window. -
APP_GUARD
: By providingThrottlerGuard
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' };
}
}
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' };
}
}
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);
}
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)