# Limitación de Tasa (Rate Limiting) **Versión:** 1.0.0 **Origen:** projects/gamilit **Estado:** Producción **Última actualización:** 2025-12-08 --- ## Descripción Sistema de limitación de tasa para proteger APIs contra abuso: - Rate limiting por IP/usuario - Configuración por endpoint - Headers HTTP estándar - Respuestas 429 con Retry-After --- ## Características | Característica | Descripción | |----------------|-------------| | Por IP | Limita requests por dirección IP | | Por Usuario | Limita requests por usuario autenticado | | Por Endpoint | Configuración individual por ruta | | Global | Límite base para toda la aplicación | | Headers Estándar | X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After | --- ## Stack Tecnológico ```yaml backend: framework: NestJS library: "@nestjs/throttler" storage: "Memory (default) | Redis (producción)" ``` --- ## Dependencias NPM ```json { "@nestjs/throttler": "^5.x" } ``` Para producción con Redis: ```json { "@nestjs/throttler-storage-redis": "^0.x", "redis": "^4.x" } ``` --- ## Configuración Básica ### En módulo principal ```typescript import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; import { APP_GUARD } from '@nestjs/core'; @Module({ imports: [ ThrottlerModule.forRoot([{ ttl: 60000, // 60 segundos limit: 100, // 100 requests por TTL }]), ], providers: [ { provide: APP_GUARD, useClass: ThrottlerGuard, }, ], }) export class AppModule {} ``` ### Configuración por ambiente ```typescript ThrottlerModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ([{ ttl: config.get('THROTTLE_TTL', 60000), limit: config.get('THROTTLE_LIMIT', 100), }]), }) ``` --- ## Uso por Endpoint ### Sobrescribir límites ```typescript import { Throttle, SkipThrottle } from '@nestjs/throttler'; @Controller('auth') export class AuthController { // Más estricto: 5 intentos por minuto @Throttle({ default: { limit: 5, ttl: 60000 } }) @Post('login') login() {} // Omitir rate limiting @SkipThrottle() @Get('public') publicEndpoint() {} } ``` ### En controlador completo ```typescript @Controller('api') @Throttle({ default: { limit: 50, ttl: 60000 } }) export class ApiController { // Todos los endpoints heredan el límite } ``` --- ## Múltiples Límites ```typescript ThrottlerModule.forRoot([ { name: 'short', ttl: 1000, // 1 segundo limit: 3, // 3 requests por segundo }, { name: 'medium', ttl: 10000, // 10 segundos limit: 20, // 20 requests por 10 segundos }, { name: 'long', ttl: 60000, // 1 minuto limit: 100, // 100 requests por minuto }, ]) ``` Usar en endpoint: ```typescript @Throttle({ short: { limit: 1, ttl: 1000 }, long: { limit: 10, ttl: 60000 }, }) @Post('expensive-operation') expensiveOperation() {} ``` --- ## Rate Limiting por Usuario ### Custom ThrottlerGuard ```typescript import { Injectable, ExecutionContext } from '@nestjs/common'; import { ThrottlerGuard } from '@nestjs/throttler'; @Injectable() export class CustomThrottlerGuard extends ThrottlerGuard { protected async getTracker(req: Record): Promise { // Si hay usuario autenticado, usar su ID if (req.user?.id) { return req.user.id; } // Fallback a IP return req.ip; } } ``` Registrar: ```typescript providers: [ { provide: APP_GUARD, useClass: CustomThrottlerGuard, }, ], ``` --- ## Almacenamiento Redis (Producción) ```typescript import { ThrottlerStorageRedisService } from '@nestjs/throttler-storage-redis'; import Redis from 'ioredis'; ThrottlerModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ throttlers: [{ ttl: config.get('THROTTLE_TTL', 60000), limit: config.get('THROTTLE_LIMIT', 100), }], storage: new ThrottlerStorageRedisService(new Redis({ host: config.get('REDIS_HOST', 'localhost'), port: config.get('REDIS_PORT', 6379), })), }), }) ``` --- ## Headers de Respuesta El módulo agrega automáticamente: ``` X-RateLimit-Limit: 100 # Límite máximo X-RateLimit-Remaining: 95 # Requests restantes X-RateLimit-Reset: 1234567890 # Timestamp de reset ``` En respuesta 429: ``` Retry-After: 60 # Segundos hasta poder reintentar ``` --- ## Excepciones Personalizadas ```typescript import { ThrottlerException } from '@nestjs/throttler'; // En el guard personalizado protected throwThrottlingException(): void { throw new ThrottlerException( 'Demasiadas solicitudes. Por favor, espere un momento.', ); } ``` --- ## Variables de Entorno ```env # Configuración básica THROTTLE_TTL=60000 # Ventana de tiempo en ms THROTTLE_LIMIT=100 # Requests por ventana # Redis (producción) REDIS_HOST=localhost REDIS_PORT=6379 ``` --- ## Casos de Uso Comunes ### 1. Login (anti brute-force) ```typescript @Throttle({ default: { limit: 5, ttl: 300000 } }) // 5 por 5 minutos @Post('login') login() {} ``` ### 2. Registro (anti spam) ```typescript @Throttle({ default: { limit: 3, ttl: 3600000 } }) // 3 por hora @Post('register') register() {} ``` ### 3. API pública ```typescript @Throttle({ default: { limit: 1000, ttl: 3600000 } }) // 1000 por hora @Get('public-data') getData() {} ``` ### 4. Endpoints costosos ```typescript @Throttle({ default: { limit: 10, ttl: 60000 } }) // 10 por minuto @Post('generate-report') generateReport() {} ``` --- ## Métricas y Logging ```typescript @Injectable() export class ThrottlerLoggerGuard extends ThrottlerGuard { protected async handleRequest( context: ExecutionContext, limit: number, ttl: number, ): Promise { const req = context.switchToHttp().getRequest(); const tracker = await this.getTracker(req); const result = await super.handleRequest(context, limit, ttl); if (!result) { this.logger.warn(`Rate limit exceeded for: ${tracker}`); } return result; } } ``` --- ## Adaptaciones Necesarias 1. **Límites**: Ajustar según tráfico esperado 2. **Storage**: Memory para desarrollo, Redis para producción 3. **Tracker**: IP vs Usuario según necesidad 4. **Mensajes**: Personalizar respuestas 429 --- ## Referencias - [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting) - [RFC 6585](https://tools.ietf.org/html/rfc6585#section-4) (429 Too Many Requests) --- **Mantenido por:** Sistema NEXUS **Proyecto origen:** Gamilit Platform