Rate Limiting
Proteja seus endpoints contra abuso com rate limiting configurável. O Bunstone oferece suporte a limitação de requisições em múltiplos níveis, com storage em memória ou Redis.
Visão Geral
O sistema de rate limiting do Bunstone oferece:
- Múltiplos níveis de configuração: Global, Controller ou Endpoint
- Storage flexível: Memória (padrão) ou Redis (produção)
- Identificação inteligente: IP + Método + Endpoint
- Headers automáticos: Informações de limites em todas as respostas
- Mensagens customizáveis: Personalize a mensagem de erro 429
Uso Básico
Por Endpoint com @RateLimit()
Use o decorator @RateLimit() para aplicar limites específicos a endpoints individuais:
import { Controller, Get, Post, RateLimit } from "@grupodiariodaregiao/bunstone";
@Controller("api")
export class ApiController {
@Get("public")
@RateLimit({ max: 100, windowMs: 60000 }) // 100 requisições/minuto
getPublic() {
return { data: [] };
}
@Post("sensitive")
@RateLimit({ max: 5, windowMs: 60000 }) // 5 requisições/minuto (mais restritivo)
createSensitive() {
return { success: true };
}
}Configuração Global
Aplique rate limiting em toda a aplicação via AppStartup.create():
const app = await AppStartup.create(AppModule, {
rateLimit: {
enabled: true,
max: 1000,
windowMs: 60000, // 1000 requisições/minuto para todos os endpoints
},
});Opções de Configuração
@RateLimit() Decorator
@RateLimit({
max: 100, // Máximo de requisições na janela
windowMs: 60000, // Janela de tempo em milissegundos (1 minuto)
message?: string, // Mensagem personalizada quando exceder (opcional)
storage?: Storage, // Storage customizado (opcional)
keyGenerator?: fn, // Função para gerar chave de identificação (opcional)
skipHeader?: string, // Header que permite bypass (opcional)
skip?: fn // Função para pular rate limit (opcional)
})Configuração Global
{
rateLimit: {
enabled?: boolean, // Habilita/desabilita rate limiting global
max?: number, // Máximo de requisições (padrão: 100)
windowMs?: number, // Janela em ms (padrão: 60000)
storage?: Storage, // Storage customizado
keyGenerator?: fn, // Gerador de chave customizado
skipHeader?: string, // Header de bypass
skip?: fn, // Função de bypass
message?: string // Mensagem de erro
}
}Storage
MemoryStorage (Padrão)
Ideal para desenvolvimento e aplicações single-instance:
// Não requer configuração - é o padrão
@RateLimit({ max: 100, windowMs: 60000 })RedisStorage
Para aplicações em produção com múltiplas instâncias:
import { RedisStorage } from "@grupodiariodaregiao/bunstone";
import Redis from "ioredis"; // ou "redis"
const redisClient = new Redis({
host: "localhost",
port: 6379,
});
const app = await AppStartup.create(AppModule, {
rateLimit: {
enabled: true,
max: 1000,
windowMs: 60000,
storage: new RedisStorage(redisClient, "ratelimit:"), // prefix opcional
},
});Headers de Resposta
Todas as respostas incluem headers informativos:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1706640000Quando o limite é excedido (HTTP 429):
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706640000
Retry-After: 45
{ "status": 429, "message": "Too many requests, please try again later." }Casos de Uso Avançados
Chave de Identificação Customizada
Por padrão, a chave é IP:Método:Path. Você pode customizar:
@RateLimit({
max: 100,
windowMs: 60000,
keyGenerator: (req) => {
// Rate limit por usuário autenticado em vez de IP
return req.headers["x-user-id"] || req.ip;
},
})Bypass via Header
Permitir bypass em ambientes internos:
@RateLimit({
max: 100,
windowMs: 60000,
skipHeader: "x-internal-request", // Requisições com este header ignoram o limit
})Bypass Condicional
Lógica customizada para pular rate limiting:
@RateLimit({
max: 100,
windowMs: 60000,
skip: (req) => {
// Pular para IPs internos
return req.ip?.startsWith("10.0.0.");
},
})Mensagens Customizadas
@RateLimit({
max: 5,
windowMs: 60000,
message: "Você atingiu o limite de tentativas. Aguarde 1 minuto.",
})Hierarquia de Configuração
As configurações são aplicadas na seguinte ordem de precedência:
- Decorator
@RateLimit()(maior precedência) - Configuração do Controller (se implementado)
- Configuração Global em
AppStartup.create() - Sem rate limit (padrão se nenhuma configuração)
Exemplo de mesclagem:
// Configuração global: 1000 req/min
const app = await AppStartup.create(AppModule, {
rateLimit: { enabled: true, max: 1000, windowMs: 60000 },
});
@Controller("api")
class ApiController {
@Get("strict")
@RateLimit({ max: 10 }) // Usa 10 req/min (sobrescreve global)
strictEndpoint() {}
@Get("default")
defaultEndpoint() {} // Usa 1000 req/min (herda global)
}Exemplo Completo
import {
Module,
Controller,
Get,
Post,
AppStartup,
RateLimit,
MemoryStorage,
} from "../../index";
/**
* Example demonstrating Rate Limiting features
*
* Features:
* - Endpoint-level rate limiting with @RateLimit()
* - Global rate limiting configuration
* - Custom rate limit messages
* - Rate limit headers in responses
*/
@Controller("api")
class ApiController {
/**
* Public endpoint with strict rate limit (5 requests per minute)
* Returns rate limit headers:
* - X-RateLimit-Limit: 5
* - X-RateLimit-Remaining: 4 (decreases with each request)
* - X-RateLimit-Reset: timestamp
*/
@Get("public")
@RateLimit({ max: 5, windowMs: 60000, message: "Too many requests. Please slow down." })
getPublic() {
return { message: "This endpoint is rate limited to 5 requests per minute" };
}
/**
* Premium endpoint with higher rate limit (100 requests per minute)
*/
@Get("premium")
@RateLimit({ max: 100, windowMs: 60000 })
getPremium() {
return { message: "Premium users get 100 requests per minute" };
}
/**
* Write operation with very strict limit (3 requests per minute)
*/
@Post("create")
@RateLimit({ max: 3, windowMs: 60000 })
createResource() {
return { message: "Resource created", id: "123" };
}
/**
* Unprotected endpoint - no rate limit applied
*/
@Get("unlimited")
getUnlimited() {
return { message: "This endpoint has no rate limiting" };
}
}
@Module({
controllers: [ApiController],
})
class AppModule {}
// Example 1: No global rate limit (only decorator-based limits)
const app1 = await AppStartup.create(AppModule);
console.log("Example 1: Decorator-only rate limits");
// Example 2: With global rate limit (applies to ALL endpoints)
const app2 = await AppStartup.create(AppModule, {
rateLimit: {
enabled: true,
max: 1000, // 1000 requests per window
windowMs: 60000, // per minute
message: "Global rate limit exceeded",
},
});
console.log("Example 2: Global rate limit (1000 req/min for all endpoints)");
// Example 3: Custom storage (Redis example - requires Redis connection)
// const redisClient = new Redis(); // from 'ioredis' or 'redis'
// const app3 = await AppStartup.create(AppModule, {
// rateLimit: {
// enabled: true,
// max: 100,
// windowMs: 60000,
// storage: new RedisStorage(redisClient), // For multi-instance deployments
// },
// });
// Start the server
const app = await AppStartup.create(AppModule);
app.listen(3000);
console.log("Rate limiting example running on http://localhost:3000");
console.log("");
console.log("Endpoints:");
console.log(" GET /api/public - 5 req/min (decorator limit)");
console.log(" GET /api/premium - 100 req/min (decorator limit)");
console.log(" POST /api/create - 3 req/min (decorator limit)");
console.log(" GET /api/unlimited - No rate limit");
console.log("");
console.log("Response headers include:");
console.log(" X-RateLimit-Limit - Maximum requests allowed");
console.log(" X-RateLimit-Remaining - Remaining requests in window");
console.log(" X-RateLimit-Reset - Unix timestamp when window resets");
console.log(" Retry-After - Seconds to wait (only when 429)");Dicas de Produção
- Use RedisStorage para aplicações multi-instância
- Configure skipHeader para health checks e monitoramento interno
- Ajuste windowMs conforme o padrão de uso (APIs REST geralmente usam 1 minuto)
- Monitore os headers para entender o padrão de uso
- Mensagens informativas ajudam usuários a entenderem os limites
API Reference
Classes
RateLimitService- Serviço principal de rate limitingMemoryStorage- Implementação em memóriaRedisStorage- Implementação Redis
Interfaces
RateLimitStorage- Interface para implementações customizadasRateLimitConfig- Configuração de rate limitRateLimitInfo- Informações de consumoRateLimitHeaders- Headers de resposta
Decorators
@RateLimit(options)- Aplica rate limit a um endpoint
Exceptions
RateLimitExceededException- Lançada quando limite é excedido