# 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<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
# .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<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:

```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<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

```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
