RateLimitGuard
Use RateLimitGuard for fine-grained control.
Manual Guard Application
import { Controller, Get, UseGuards } from '@nestjs/common';
import { RateLimitGuard } from '@nestjs-redisx/rate-limit';
@Controller('api')
@UseGuards(RateLimitGuard)
export class ApiController {
@Get('data')
getData() {
return { data: 'value' };
}
}Guard Ordering
Guards execute in the order they are specified. Rate limit should typically run AFTER authentication:
import { AuthGuard } from '@nestjs/passport';
import { RateLimitGuard } from '@nestjs-redisx/rate-limit';
@Controller('api')
@UseGuards(AuthGuard('jwt'), RateLimitGuard)
@RateLimit({ key: 'user', points: 100, duration: 60 })
export class ApiController {
@Get('protected')
getProtectedData() {
// Rate limit uses request.user.id set by AuthGuard
return { protected: true };
}
}Execution Order
Global Guard
Apply to all endpoints via module (required for DI):
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { RedisModule } from '@nestjs-redisx/core';
import { RateLimitPlugin, RateLimitGuard } from '@nestjs-redisx/rate-limit';
@Module({
imports: [
RedisModule.forRoot({
clients: { host: 'localhost', port: 6379 },
plugins: [
new RateLimitPlugin({
defaultPoints: 100,
defaultDuration: 60,
}),
],
}),
],
providers: [
{
provide: APP_GUARD,
useClass: RateLimitGuard,
},
],
})
export class AppModule {}WARNING
Do not use app.useGlobalGuards(new RateLimitGuard()) — the guard requires DI (RateLimitService, Reflector, module options). Use APP_GUARD with useClass instead.
Skipping Rate Limits
Without Global Guard
When using @RateLimit per-endpoint, simply don't apply the decorator to endpoints that should be unlimited:
@Controller('api')
export class ApiController {
@Get('limited')
@RateLimit({ points: 10, duration: 60 })
limited() {
return { limited: true };
}
@Get('unlimited')
// No @RateLimit — no guard attached, no rate limiting
unlimited() {
return { unlimited: true };
}
}With Global Guard
When using APP_GUARD, the guard runs on all endpoints. Use the skip option to bypass specific routes:
@Controller('api')
export class ApiController {
@Get('limited')
@RateLimit({ points: 10, duration: 60 })
limited() {
return { limited: true };
}
@Get('unlimited')
@RateLimit({ skip: () => true })
unlimited() {
return { unlimited: true };
}
}Skip by Condition
Use skip for conditional bypassing (e.g., admin users):
@Get('data')
@RateLimit({
points: 10,
duration: 60,
skip: (ctx) => {
const req = ctx.switchToHttp().getRequest();
return req.user?.role === 'admin';
},
})
getData() {
return { data: 'value' };
}Guard Context
Access execution context within guard:
import { Injectable, ExecutionContext, CanActivate } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class CustomRateLimitGuard implements CanActivate {
constructor(private reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
// Skip for admins
if (request.user?.role === 'admin') {
return true;
}
// Custom rate limit logic
const key = `user:${request.user?.id || request.ip}`;
// Check rate limit...
return true;
}
}GraphQL Support
Rate limit GraphQL resolvers (works because GraphQL over HTTP uses HTTP context):
import { Resolver, Query, UseGuards } from '@nestjs/graphql';
import { RateLimitGuard } from '@nestjs-redisx/rate-limit';
@Resolver()
@UseGuards(RateLimitGuard)
export class UserResolver {
@Query(() => [User])
@RateLimit({ points: 100, duration: 60 })
async users() {
return this.userService.findAll();
}
}WebSocket Support
WARNING
The built-in RateLimitGuard uses context.switchToHttp() for both key extraction and setting response headers. For WebSocket contexts, you must:
- Provide a custom
keyfunction that handles the WebSocket execution context - Set
includeHeaders: falseinRateLimitPluginoptions (since WebSocket has no HTTP response headers)
// Plugin config — disable HTTP headers for WebSocket
new RateLimitPlugin({
includeHeaders: false, // Required for non-HTTP contexts
})import { WebSocketGateway, SubscribeMessage } from '@nestjs/websockets';
import { UseGuards } from '@nestjs/common';
import { RateLimitGuard, RateLimit } from '@nestjs-redisx/rate-limit';
@WebSocketGateway()
@UseGuards(RateLimitGuard)
export class ChatGateway {
@SubscribeMessage('message')
@RateLimit({
points: 10,
duration: 60,
key: (ctx) => {
const client = ctx.switchToWs().getClient();
return `ws:${client.id}`;
},
})
handleMessage(client: any, payload: any) {
return { event: 'message', data: payload };
}
}Microservices Support
WARNING
For microservice contexts, you must:
- Provide a custom
keyfunction — the built-in key extractors (ip,user,apiKey) rely on HTTP request objects - Set
includeHeaders: falseinRateLimitPluginoptions (since RPC contexts have no HTTP response headers)
// Plugin config — disable HTTP headers for microservice contexts
new RateLimitPlugin({
includeHeaders: false, // Required for non-HTTP contexts
})import { Controller, UseGuards } from '@nestjs/common';
import { MessagePattern, Ctx, RmqContext } from '@nestjs/microservices';
import { RateLimitGuard, RateLimit } from '@nestjs-redisx/rate-limit';
@Controller()
@UseGuards(RateLimitGuard)
export class UserController {
@MessagePattern({ cmd: 'get_user' })
@RateLimit({
points: 1000,
duration: 60,
key: (ctx) => {
const data = ctx.switchToRpc().getData();
return `rpc:${data.id || 'unknown'}`;
},
})
async getUser(data: { id: string }) {
return this.userService.findOne(data.id);
}
}Next Steps
- Service API — Programmatic API
- Key Extraction — Custom keys