Sistema NEXUS v3.4 migrado con: Estructura principal: - core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles) - core/catalog: Catalogo de funcionalidades reutilizables - shared/knowledge-base: Base de conocimiento compartida - devtools/scripts: Herramientas de desarrollo - control-plane/registries: Control de servicios y CI/CD - orchestration/: Configuracion de orquestacion de agentes Proyectos incluidos (11): - gamilit (submodule -> GitHub) - trading-platform (OrbiquanTIA) - erp-suite con 5 verticales: - erp-core, construccion, vidrio-templado - mecanicas-diesel, retail, clinicas - betting-analytics - inmobiliaria-analytics - platform_marketing_content - pos-micro, erp-basico Configuracion: - .gitignore completo para Node.js/Python/Docker - gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git) - Sistema de puertos estandarizado (3005-3199) Generated with NEXUS v3.4 Migration System EPIC-010: Configuracion Git y Repositorios
8.1 KiB
8.1 KiB
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
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
// 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
// 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<number>('THROTTLE_TTL', 60000),
limit: config.get<number>('THROTTLE_LIMIT', 100),
}]),
}),
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
],
})
export class AppModule {}
Paso 3: Variables de Entorno
# .env
THROTTLE_TTL=60000
THROTTLE_LIMIT=100
Paso 4: Personalizar por Endpoint
Limitar endpoint específico
// 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
import { SkipThrottle } from '@nestjs/throttler';
@Controller('health')
export class HealthController {
@SkipThrottle()
@Get()
check() {
return { status: 'ok' };
}
}
Paso 5: Rate Limiting por Usuario (Opcional)
// 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<string, any>): Promise<string> {
// 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:
providers: [
{
provide: APP_GUARD,
useClass: CustomThrottlerGuard, // En lugar de ThrottlerGuard
},
],
Paso 6: Múltiples Límites (Opcional)
// 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:
@Throttle({
short: { limit: 1, ttl: 1000 },
long: { limit: 10, ttl: 60000 },
})
@Post('heavy-operation')
async heavyOperation() {
// ...
}
Paso 7: Redis para Producción (Opcional)
npm install @nestjs/throttler-storage-redis ioredis
// 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:
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
Paso 8: Logging de Rate Limits (Opcional)
// 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<boolean> {
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
ThrottlerModule.forRoot([{
ttl: 60000,
limit: 1000, // Alto para no molestar durante desarrollo
}])
Producción
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
# 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