req-guard-lite: Un limitador de tasas mínimo y basado en TypeScript para Express
Aprende cómo funciona un limitador de tasas Express ligero y sin dependencias, desde los valores predeterminados en memoria hasta la escalabilidad con Redis y generadores de claves personalizados.
Toda aplicación Express llega eventualmente a un punto en el que necesita limitación de tasas.
Ya sea con el objetivo de proteger las rutas de inicio de sesión, reducir el spam o simplemente evitar un uso excesivo sin querer, la limitación del tráfico entrante pasa rápidamente de ser algo conveniente a ser una necesidad una vez que una API se expone al público.
Mientras buscaba una solución de limitación de tasas que satisficiera un conjunto común de necesidades, quedó claro que, aunque ya existen muchas bibliotecas sólidas, muchos proyectos en realidad solo quieren algo pequeño, fácil de entender y sencillo de personalizar.
Ese vacío fue lo que llevó a la creación de req-guard-lite.
¿Por qué otro limitador de tasas?
La mayoría de las APIs no necesitan un conjunto completo de soluciones de seguridad empresarial desde el primer día.
A menudo, lo único que se desea es poder escribir algo como:
app.use(rateLimit({
max: 100,
windowMs: 15 * 60 * 1000
}));
...y volver a trabajar en la construcción del resto de la aplicación.
Los objetivos de diseño para este paquete fueron:
- Mantenerse ligero
- Ser fácil de configurar
- Se construyó teniendo en cuenta TypeScript desde el principio
- Permitir una extensión sencilla
- Funcionar tanto en proyectos pequeños como en sistemas a gran escala
Presentamos req-guard-lite
req-guard-lite es un middleware compacto de Express diseñado para proteger su API de una cantidad abrumadora de solicitudes.
Por defecto funciona íntegramente en memoria, pero también puede escalar a configuraciones distribuidas al conectarse con Redis.
Su alcance es intencionalmente limitado: hace una cosa muy bien:
Registra las solicitudes entrantes y rechaza a los clientes una vez que superan el límite que haya establecido.
Características
Implementación ligera Cero dependencias en tiempo de ejecución en el paquete principal Funciona como middleware de Express Soporte nativo para TypeScript Integración con Redis disponible Soporte para backends de almacenamiento personalizados Soporte para generadores de claves personalizados
Comenzando
Instálalo como complemento de Express.
npm install req-guard-lite express
A continuación, conéctalo a tu aplicación.
import express from 'express';
import { rateLimit } from 'req-guard-lite';
const app = express();
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: 'Too many requests, please try again later.'
});
app.use(limiter);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000);
Esa es toda la configuración.
Tu API ahora bloquea a cualquier cliente que envíe más de 100 solicitudes dentro de un período de 15 minutos.
Almacenamiento en memoria por defecto
Por defecto, los contadores de solicitudes se almacenan en memoria.
En la práctica, eso significa:
- No se requiere Redis
- No se requiere base de datos
- No se necesita configuración adicional
- Ideal para el desarrollo local
- Elección sólida para producción en un único servidor
Para una gran parte de las aplicaciones, este es todo el control de velocidad que se necesitará.
Escalar con Redis
Cuando una aplicación se expande para ejecutarse en varios servidores o contenedores, esas instancias necesitan una visión compartida del número de solicitudes.
Ese es el papel que desempeña Redis aquí.
import Redis from "ioredis";
import { createRedisStore } from "req-guard-lite/redis";
const redis = new Redis();
const limiter = rateLimit({
max: 100,
windowMs: 15 * 60 * 1000,
store: createRedisStore(redis, {
max: 100,
windowMs: 15 * 60 * 1000
})
});
Con esto en marcha, cada instancia del servidor lee y escribe los mismos contadores, por lo que los límites se mantienen consistentes sin importar qué nodo maneje una solicitud determinada.
Generadores de claves personalizados
El control de velocidad por dirección IP no siempre es el enfoque adecuado.
En algunos casos, es mejor basar los límites en:
- Un ID de usuario
- Una clave API
- Un identificador de inquilino
- Una organización
- Un claim de sujeto JWT
- O cualquier otro identificador que se ajuste a su modelo
Para ello, req-guard-lite le permite proporcionar su propia función generadora de claves.
const limiter = rateLimit({
max: 100,
keyGenerator: (req) =>
req.headers["x-api-key"] as string
});
O, en su lugar, basado en el usuario conectado:
const limiter = rateLimit({
max: 50,
keyGenerator: (req) =>
(req as any).user.id
});
El middleware en sí es ajeno a lo que representa la clave; simplemente mantiene un recuento basado en el identificador que devuelva su función.
Utilice su propio almacén
La posibilidad de extensión fue un requisito fundamental desde el primer día.
En lugar de obligarlo a usar Redis, req-guard-lite ofrece una interfaz sencilla RateLimitStore. Si su infraestructura ya depende de:
- PostgreSQL
- DynamoDB
- Memcached
- MongoDB
- SQLite
- Otra capa de caché personalizada
puede integrarla implementando esa única interfaz.
class MyStore implements RateLimitStore {
consume(key: string) {
// your implementation
}
}
Diseño que mantiene el paquete adaptable a casi cualquier backend que ya esté en uso.
Un consejo importante para producción
Si su aplicación se encuentra detrás de:
- Nginx
- Un balanceador de carga de AWS
- Heroku
- Cloudflare
- Cualquier proxy inverso
asegúrese de configurar Express correctamente:
app.set("trust proxy", 1);
Si omite este paso, Express a menudo tratará cada solicitud entrante como si proviniera del propio proxy, lo que significa que todos sus usuarios terminarán compartiendo un único límite de velocidad. Es una solución sencilla, pero evita problemas en producción que sorprenden a muchos equipos.
Cómo funciona
El flujo interno es deliberadamente mínimo:
- Llega una solicitud.
- El middleware genera una clave para ella (la IP del cliente, por defecto).
- El almacén activo incrementa el contador asociado a esa clave.
Dado que el sistema es modular, este mismo flujo funciona independientemente de si se basa en memoria, Redis o en una implementación personalizada.
¿Por qué TypeScript?
Toda la biblioteca está escrita en TypeScript, lo que ofrece:
- Tipado estricto en todo momento
- Completado automático más completo en el editor
- Mantenimiento a largo plazo más sencillo
- APIs que son más difíciles de usar incorrectamente
Los usuarios de TypeScript cuentan con definiciones de tipo completas desde el principio, sin necesidad de instalar paquetes adicionales @types.
Hoja de ruta
El desarrollo continúa, y ya se han planificado algunas funcionalidades.
v0.4.0
- Soporte para los encabezados de respuesta estándar de limitación de velocidad
X-RateLimit-Limit
X-RateLimit-Remaining
X-RateLimit-Reset
Estos encabezados permiten a las aplicaciones cliente conocer cuántas solicitudes quedan antes de alcanzar el límite máximo.
v0.5.0
Ganchos configurables que se activan cuando se excede un límite, útiles para cosas como:
- Registro de logs
- Recopilación de métricas
- Envío de alertas
- Análisis de datos
- Envío de información a herramientas externas de monitoreo
Por qué es código abierto
Este proyecto no surge como reacción a las limitaciones de las bibliotecas existentes; ya hay varias soluciones excelentes para el control de tasas en el ecosistema Node. req-guard-lite existe porque su objetivo era crear un paquete que sea:
- Suficientemente compacto como para leerlo y comprenderlo de un vistazo
- Fácil de extender
- Diseñado priorizando TypeScript
- Livre de complejidades innecesarias
- Suficientemente flexible como para escalar según las necesidades reales en producción
Desarrollarlo también ha sido una práctica valiosa para publicar paquetes, diseñar APIs, escribir pruebas, integrarse con Redis y crear abstracciones que resulten cómodas de usar para otros desarrolladores.
Notas finales
Hacer open source de un proyecto es una de las formas más efectivas de mejorar tus habilidades técnicas. Una vez que otros desarrolladores pueden instalar, utilizar y contribuir a tu código, te ves obligado a pensar más allá de tu propio caso de uso inmediato: la documentación, el diseño de APIs, las pruebas, la versionado y la compatibilidad con versiones anteriores se convierten en limitaciones reales que debes tener en cuenta al diseñar.
req-guard-lite comenzó como un pequeño middleware creado para satisfacer una necesidad personal, pero la esperanza es que se convierta en una opción útil, ligera y extensible para limitar la tasa de solicitudes destinada a otros desarrolladores de Express. Se aceptan comentarios, sugerencias de funcionalidades y contribuciones.
Lecturas relacionadas
- Compartir un esquema Zod entre tu frontend React y backend Node — Aprende cómo un único esquema Zod puede validar formularios de React, respuestas de API, cuerpos de solicitud de Express y variables de entorno, al mismo tiempo que genera tipos TypeScript correspondientes.
- Zod vs express-validator: Dos enfoques para la validación en Express — Compara la validación de solicitudes basada en esquemas con Zod frente a los middleware basados en cadenas de express-validator, abordando la configuración, el formato de los errores y las trampas más comunes.