Inicio / Artículos / req-guard-lite: Un limitador de tasas mínimo y basado en TypeScript para Express

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.

1291 palabras

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:

  1. Llega una solicitud.
  2. El middleware genera una clave para ella (la IP del cliente, por defecto).
  3. El almacén activo incrementa el contador asociado a esa clave.
  • Una vez que el contador supera el umbral configurado, el middleware responde con HTTP 429 Too Many Requests.
  • Si no se ha alcanzado el límite, la solicitud pasa sin ser modificada.
  • 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