workspace-v1/shared/catalog/rate-limiting/README.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

6.5 KiB

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

backend:
  framework: NestJS
  library: "@nestjs/throttler"
  storage: "Memory (default) | Redis (producción)"

Dependencias NPM

{
  "@nestjs/throttler": "^5.x"
}

Para producción con Redis:

{
  "@nestjs/throttler-storage-redis": "^0.x",
  "redis": "^4.x"
}

Configuración Básica

En módulo principal

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

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

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

@Controller('api')
@Throttle({ default: { limit: 50, ttl: 60000 } })
export class ApiController {
  // Todos los endpoints heredan el límite
}

Múltiples Límites

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:

@Throttle({
  short: { limit: 1, ttl: 1000 },
  long: { limit: 10, ttl: 60000 },
})
@Post('expensive-operation')
expensiveOperation() {}

Rate Limiting por Usuario

Custom ThrottlerGuard

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, usar su ID
    if (req.user?.id) {
      return req.user.id;
    }
    // Fallback a IP
    return req.ip;
  }
}

Registrar:

providers: [
  {
    provide: APP_GUARD,
    useClass: CustomThrottlerGuard,
  },
],

Almacenamiento Redis (Producción)

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

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

# 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)

@Throttle({ default: { limit: 5, ttl: 300000 } }) // 5 por 5 minutos
@Post('login')
login() {}

2. Registro (anti spam)

@Throttle({ default: { limit: 3, ttl: 3600000 } }) // 3 por hora
@Post('register')
register() {}

3. API pública

@Throttle({ default: { limit: 1000, ttl: 3600000 } }) // 1000 por hora
@Get('public-data')
getData() {}

4. Endpoints costosos

@Throttle({ default: { limit: 10, ttl: 60000 } }) // 10 por minuto
@Post('generate-report')
generateReport() {}

Métricas y Logging

@Injectable()
export class ThrottlerLoggerGuard extends ThrottlerGuard {
  protected async handleRequest(
    context: ExecutionContext,
    limit: number,
    ttl: number,
  ): Promise<boolean> {
    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


Mantenido por: Sistema NEXUS Proyecto origen: Gamilit Platform