TypeScript bajo el capó: Rate limiting y throttling sin dolor

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Cómo evitar que su API colapse bajo una avalancha de solicitudes

\n\n

Imagine: lanzó un servicio genial, escribió el backend en TypeScript, y de repente — ¡bam! — un usuario «amable» ejecuta un script que golpea su endpoint con miles de solicitudes por segundo. El servidor cae, la base de datos se ahoga, y los usuarios honestos ven un error 502. ¿Le suena?

\n\n

En el mundo del desarrollo web, rate limiting (limitación de frecuencia de solicitudes) y throttling (estrangulamiento) no son solo palabras de moda, sino un salvavidas para su API. Hoy analizaremos cómo implementar estos mecanismos en TypeScript sin romperse la cabeza. ¡Allá vamos!

\n\n

Rate limiting vs Throttling: ¿cuál es la diferencia?

\n\n

Antes de escribir código, aclaremos los conceptos. Muchos los confunden, pero la diferencia es fundamental.

\n\n
    \n
  • Rate limiting — es un límite estricto: «No más de 100 solicitudes por minuto desde una misma IP». ¿Lo superó? Recibe un 429 Too Many Requests y bloqueo hasta el final de la ventana. Funciona a nivel de API gateway.
  • \n
  • Throttling — es una regulación suave: «Puede hacer solicitudes, pero no más de una por segundo». Las demás solicitudes se ponen en cola o se ignoran, pero el usuario no es bloqueado.
  • \n
\n\n

Una analogía simple: rate limiting es un torniquete de metro (pasó la tarjeta — pasa, no — se queda), mientras que throttling es un controlador de tráfico en un cruce (los coches avanzan, pero con intervalo).

\n\n

¿Cuándo se necesita? Escenarios reales

\n\n

Udirá: «Tengo un sitio pequeño, ¿para qué necesito esto?» Pues para esto:

\n\n
    \n
  • Protección contra DDoS y fuerza bruta — los atacantes no podrán probar contraseñas ni tumbar el servidor.
  • \n
  • Ahorro de recursos — las API de pago (por ejemplo, OpenAI) cobran por cada llamada. Al limitar a los clientes, salvará su presupuesto.
  • \n
  • Distribución justa de la carga — un usuario «ruidoso» no aplastará a todos los demás.
  • \n
  • Cumplimiento del SLA — si promete un uptime del 99.9%, no puede prescindir de las limitaciones.
  • \n
\n\n

Escribiendo rate limiting en TypeScript: un inicio simple

\n\n

Empecemos con un ejemplo básico — un rate limiter en memoria basado en ventana fija (Fixed Window). Almacena contadores de solicitudes directamente en la memoria de la aplicación.

\n\n
// rateLimiter.ts\nimport { Request, Response, NextFunction } from 'express';\n\ninterface RateLimitEntry {\n    count: number;\n    resetTime: number;\n}\n\nclass InMemoryRateLimiter {\n    private store: Map<string, RateLimitEntry> = new Map();\n    private windowMs: number;\n    private maxRequests: number;\n\n    constructor(windowMs: number, maxRequests: number) {\n        this.windowMs = windowMs;\n        this.maxRequests = maxRequests;\n    }\n\n    public middleware() {\n        return (req: Request, res: Response, next: NextFunction): void => {\n            const key = req.ip || 'unknown';\n            const now = Date.now();\n            let entry = this.store.get(key);\n\n            if (!entry || now > entry.resetTime) {\n                // Nueva ventana\n                entry = { count: 0, resetTime: now + this.windowMs };\n                this.store.set(key, entry);\n            }\n\n            entry.count++;\n\n            if (entry.count > this.maxRequests) {\n                res.status(429).json({\n                    error: 'Too Many Requests',\n                    retryAfter: Math.ceil((entry.resetTime - now) / 1000)\n                });\n                return;\n            }\n\n            // Pasamos información del límite en los encabezados\n            res.setHeader('X-RateLimit-Limit', this.maxRequests);\n            res.setHeader('X-RateLimit-Remaining', this.maxRequests - entry.count);\n            res.setHeader('X-RateLimit-Reset', Math.ceil(entry.resetTime / 1000));\n\n            next();\n        };\n    }\n\n    // Limpieza de registros antiguos (para evitar fugas de memoria)\n    public cleanup(): void {\                

Blogs

Book Recommendations