workspace-v1/shared/catalog/rate-limiting/IMPLEMENTATION.md
rckrdmrd cb4c0681d3 feat(workspace): Add new projects and update architecture
New projects created:
- michangarrito (marketplace mobile)
- template-saas (SaaS template)
- clinica-dental (dental ERP)
- clinica-veterinaria (veterinary ERP)

Architecture updates:
- Move catalog from core/ to shared/
- Add MCP servers structure and templates
- Add git management scripts
- Update SUBREPOSITORIOS.md with 15 new repos
- Update .gitignore for new projects

Repository infrastructure:
- 4 main repositories
- 11 subrepositorios
- Gitea remotes configured

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 04:43:28 -06:00

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