# Guía de Implementación: Rate Limiting **Versión:** 1.0.0 **Tiempo estimado:** 30 min - 1 hora **Complejidad:** Baja --- ## Pre-requisitos - [ ] Proyecto NestJS existente - [ ] (Opcional) Redis para producción --- ## Paso 1: Instalar Dependencias ```bash npm install @nestjs/throttler # Para producción con Redis (opcional) npm install @nestjs/throttler-storage-redis redis ``` --- ## Paso 2: Configurar Módulo ### Opción A: Configuración Simple ```typescript // src/app.module.ts import { Module } from '@nestjs/common'; 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 minuto }]), ], providers: [ { provide: APP_GUARD, useClass: ThrottlerGuard, }, ], }) export class AppModule {} ``` ### Opción B: Configuración con Ambiente ```typescript // src/app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; import { APP_GUARD } from '@nestjs/core'; @Module({ imports: [ ConfigModule.forRoot(), ThrottlerModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ([{ ttl: config.get('THROTTLE_TTL', 60000), limit: config.get('THROTTLE_LIMIT', 100), }]), }), ], providers: [ { provide: APP_GUARD, useClass: ThrottlerGuard, }, ], }) export class AppModule {} ``` --- ## Paso 3: Variables de Entorno ```env # .env THROTTLE_TTL=60000 THROTTLE_LIMIT=100 ``` --- ## Paso 4: Personalizar por Endpoint ### Limitar endpoint específico ```typescript // src/modules/auth/controllers/auth.controller.ts import { Controller, Post, Body } from '@nestjs/common'; import { Throttle } from '@nestjs/throttler'; @Controller('auth') export class AuthController { // Solo 5 intentos de login por minuto @Throttle({ default: { limit: 5, ttl: 60000 } }) @Post('login') async login(@Body() dto: LoginDto) { return this.authService.login(dto); } // Solo 3 registros por hora (anti-spam) @Throttle({ default: { limit: 3, ttl: 3600000 } }) @Post('register') async register(@Body() dto: RegisterDto) { return this.authService.register(dto); } } ``` ### Excluir endpoint del rate limiting ```typescript import { SkipThrottle } from '@nestjs/throttler'; @Controller('health') export class HealthController { @SkipThrottle() @Get() check() { return { status: 'ok' }; } } ``` --- ## Paso 5: Rate Limiting por Usuario (Opcional) ```typescript // src/common/guards/custom-throttler.guard.ts 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, limitar por usuario if (req.user?.id) { return `user-${req.user.id}`; } // Si no, limitar por IP return req.ip; } // Opcional: Personalizar mensaje de error protected throwThrottlingException(): void { throw new HttpException( { statusCode: 429, message: 'Demasiadas solicitudes. Intente de nuevo más tarde.', error: 'Too Many Requests', }, HttpStatus.TOO_MANY_REQUESTS, ); } } ``` Registrar en app.module.ts: ```typescript providers: [ { provide: APP_GUARD, useClass: CustomThrottlerGuard, // En lugar de ThrottlerGuard }, ], ``` --- ## Paso 6: Múltiples Límites (Opcional) ```typescript // src/app.module.ts ThrottlerModule.forRoot([ { name: 'short', ttl: 1000, // 1 segundo limit: 3, // Máximo 3 por segundo (anti-DDoS básico) }, { name: 'medium', ttl: 10000, // 10 segundos limit: 20, // Máximo 20 por 10 segundos }, { name: 'long', ttl: 60000, // 1 minuto limit: 100, // Máximo 100 por minuto }, ]) ``` Usar en endpoint: ```typescript @Throttle({ short: { limit: 1, ttl: 1000 }, long: { limit: 10, ttl: 60000 }, }) @Post('heavy-operation') async heavyOperation() { // ... } ``` --- ## Paso 7: Redis para Producción (Opcional) ```bash npm install @nestjs/throttler-storage-redis ioredis ``` ```typescript // src/app.module.ts 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), password: config.get('REDIS_PASSWORD'), }), ), }), }) ``` Variables de entorno adicionales: ```env REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= ``` --- ## Paso 8: Logging de Rate Limits (Opcional) ```typescript // src/common/guards/throttler-logger.guard.ts import { Injectable, ExecutionContext, Logger } from '@nestjs/common'; import { ThrottlerGuard } from '@nestjs/throttler'; @Injectable() export class ThrottlerLoggerGuard extends ThrottlerGuard { private readonly logger = new Logger('RateLimit'); protected async handleRequest( context: ExecutionContext, limit: number, ttl: number, throttler: any, getTracker: any, generateKey: any, ): Promise { const result = await super.handleRequest( context, limit, ttl, throttler, getTracker, generateKey, ); if (!result) { const req = context.switchToHttp().getRequest(); this.logger.warn( `Rate limit exceeded: ${req.ip} - ${req.method} ${req.url}`, ); } return result; } } ``` --- ## Configuraciones Recomendadas ### Desarrollo ```typescript ThrottlerModule.forRoot([{ ttl: 60000, limit: 1000, // Alto para no molestar durante desarrollo }]) ``` ### Producción ```typescript ThrottlerModule.forRoot([ { name: 'short', ttl: 1000, limit: 10 }, { name: 'long', ttl: 60000, limit: 100 }, ]) ``` ### Por tipo de endpoint | Tipo | Límite Recomendado | |------|-------------------| | Login | 5 por minuto | | Registro | 3 por hora | | API pública | 100 por minuto | | API autenticada | 1000 por minuto | | Operaciones costosas | 10 por minuto | | Webhooks | Sin límite (SkipThrottle) | | Health check | Sin límite (SkipThrottle) | --- ## Checklist de Implementación - [ ] @nestjs/throttler instalado - [ ] ThrottlerModule configurado en AppModule - [ ] ThrottlerGuard registrado como APP_GUARD - [ ] Variables de entorno configuradas - [ ] Límites ajustados para endpoints críticos (login, register) - [ ] Endpoints públicos excluidos (health, webhooks) - [ ] Redis configurado (producción) - [ ] Build pasa sin errores - [ ] Test manual: verificar respuesta 429 --- ## Verificar Funcionamiento ```bash # Test simple con curl for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/api/test; done # Debería mostrar 200 hasta el límite, luego 429 ``` --- ## Troubleshooting ### Error: "ThrottlerModule is not imported" Asegurarse de importar `ThrottlerModule.forRoot()` en AppModule. ### Rate limiting no funciona Verificar que `ThrottlerGuard` esté registrado como `APP_GUARD`. ### 429 en todos los requests El límite es muy bajo. Aumentar `limit` en configuración. ### Memory leaks en producción Usar Redis como storage en lugar de memoria. --- ## Código de Referencia Estructura típica: ``` src/ ├── app.module.ts # ThrottlerModule configurado ├── common/ │ └── guards/ │ └── custom-throttler.guard.ts # Guard personalizado └── modules/ └── auth/ └── controllers/ └── auth.controller.ts # @Throttle en endpoints ``` --- **Versión:** 1.0.0 **Sistema:** SIMCO Catálogo