Accueil / Articles / req-guard-lite : Un limiteur de débit minimal, basé sur TypeScript, pour Express

req-guard-lite : Un limiteur de débit minimal, basé sur TypeScript, pour Express

Découvrez comment fonctionne un limiteur de débit Express léger et sans dépendances, des paramètres par défaut en mémoire jusqu’à l’escalade via Redis et aux générateurs de clés personnalisés.

1291 mots

Toute application Express atteint tôt ou tard un stade où une limitation de fréquence devient nécessaire.

Que l’objectif soit de protéger les routes d’accès, de réduire le spam ou simplement d’éviter une utilisation excessive non intentionnelle, la limitation du trafic entrant passe rapidement d’une fonction utile à une exigence dès qu’une API est mise en ligne publiquement.

Lors de la recherche d’une solution de limitation de fréquence répondant à des besoins courants, il est apparu clairement que, bien qu’il existe déjà de nombreuses bibliothèques performantes, de nombreux projets souhaitent en réalité quelque chose de léger, facile à comprendre et simple à personnaliser.

C’est ce manque qui a conduit à la création de req-guard-lite.

Pourquoi un autre limiteur de fréquence ?

La plupart des API n’ont pas besoin d’un ensemble complet de solutions de sécurité d’entreprise dès le premier jour.

Souvent, tout ce dont on a besoin, c’est de pouvoir écrire quelque chose comme :

app.use(rateLimit({
  max: 100,
  windowMs: 15 * 60 * 1000
}));

...et revenir à la construction du reste de l’application.

Les objectifs de conception pour ce package étaient :

  • Rester léger
  • Être facile à mettre en place
  • Être conçu dès le départ avec TypeScript en tête
  • Permettre une extension facile
  • Fonctionner aussi bien pour de petits projets que pour des systèmes à grande échelle

Présentation de req-guard-lite

req-guard-lite est un middleware Express compact conçu pour protéger votre API d’un volume écrasant de requêtes.

Par défaut, il fonctionne entièrement en mémoire, mais il peut également être étendu à des configurations distribuées en s’intégrant à Redis.

Son champ d’action est intentionnellement restreint — il fait bien une seule chose :

Suivre les requêtes entrantes et rejeter les clients dès qu’ils dépassent la limite que vous avez fixée.

Fonctionnalités

Mise en œuvre légère Aucune dépendance en temps de exécution dans le package principal Fonctionne comme middleware Express Soutien natif pour TypeScript Intégration Redis disponible Soutien pour des backends de stockage personnalisés Soutien pour des générateurs de clés personnalisés

Démarrer

Installez-le en complément d’Express.

npm install req-guard-lite express

Ensuite, connectez-le à votre application.

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

C’est toute la configuration nécessaire.

Votre API bloque désormais tout client envoyant plus de 100 requêtes dans une fenêtre de 15 minutes.

Stockage en mémoire par défaut

Déjà prêt à l’emploi, les compteurs de requêtes sont stockés en mémoire.

En pratique, cela signifie :

  • Aucun Redis requis
  • Aucune base de données requise
  • Aucune configuration supplémentaire nécessaire
  • Ideal pour le développement local
  • Sélection fiable pour la production sur un seul serveur

Pour une grande partie des applications, c’est toute la limitation de débit dont vous aurez jamais besoin.

Échelle avec Redis

Lorsqu’une application s’étend pour fonctionner sur plusieurs serveurs ou conteneurs, ces instances ont besoin d’une vue partagée du nombre de requêtes.

C’est là que Redis joue son rôle.

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
    })
});

Avec cela en place, chaque instance serveur lit et écrit les mêmes compteurs, de sorte que les limites restent cohérentes quel que soit le nœud qui traite une requête donnée.

Générateurs de clés personnalisés

La limitation de débit par adresse IP n’est pas toujours la bonne approche.

Dans certains cas, il est préférable de baser les limites sur :

  • Un ID d’utilisateur
  • Une clé API
  • Un identifiant de locataire
  • Une organisation
  • Une prétention de sujet JWT
  • Ou tout autre identifiant adapté à votre modèle

Pour cela, req-guard-lite vous permet de fournir votre propre fonction générateur de clé.

const limiter = rateLimit({
    max: 100,
    keyGenerator: (req) =>
        req.headers["x-api-key"] as string
});

Ou, en se basant sur l’utilisateur connecté plutôt :

const limiter = rateLimit({
    max: 50,
    keyGenerator: (req) =>
        (req as any).user.id
});

Le middleware lui-même est indifférent à la signification de la clé — il se contente de compter les appels en fonction de l’identifiant retourné par votre fonction.

Apportez votre propre stockage

L’extensibilité était une exigence fondamentale dès le début.

Au lieu de vous limiter à Redis, req-guard-lite expose une interface simple RateLimitStore. Si votre infrastructure repose déjà sur :

  • PostgreSQL
  • DynamoDB
  • Memcached
  • MongoDB
  • SQLite
  • Un autre layer de mise en cache personnalisé

vous pouvez l’intégrer en implémentant cette seule interface.

class MyStore implements RateLimitStore {
    consume(key: string) {
        // your implementation
    }
}

Cette conception permet au package de s’adapter à presque n’importe quel backend déjà en cours d’exécution.

Un conseil important pour la production

Si votre application est derrière :

  • Nginx
  • Un chargeur de balançage AWS
  • Heroku
  • Cloudflare
  • Tout proxy inversé

assurez-vous de configurer Express correctement :

app.set("trust proxy", 1);

Si vous sautez cette étape, Express traitera souvent chaque requête entrante comme si elle provenait du proxy lui-même, ce qui signifie que tous vos utilisateurs partageront le même quota de requêtes. La solution est simple, mais elle évite un problème en production qui prend souvent par surprise de nombreuses équipes.

Fonctionnement

Le flux interne est délibérément minimal :

  1. Une requête arrive.
  2. Le middleware génère une clé pour elle (l’IP du client, par défaut).
  3. Le stock actif incrémente le compteur associé à cette clé.
  • Lorsque le compteur dépasse le seuil configuré, le middleware répond par une erreur HTTP 429 Too Many Requests.
  • Si la limite n’a pas été atteinte, la requête est transmise sans modification.
  • Comme le système est adaptable, ce même flux fonctionne que vous utilisiez de la mémoire, Redis ou une implémentation personnalisée.

    Pourquoi TypeScript ?

    Toute la bibliothèque est écrite en TypeScript, ce qui offre :

    • Un typage strict partout
    • Une complétion automatique plus riche dans l’éditeur
    • Un entretien à long terme simplifié
    • Des API plus difficiles à utiliser de manière abusive

    Les utilisateurs de TypeScript disposent de définitions de types complètes dès le départ, sans avoir besoin d’installer de packages @types supplémentaires.

    Feuille de route

    v0.4.0

    • Soutien aux en-têtes de réponse standard de limitation de fréquence
    X-RateLimit-Limit
    X-RateLimit-Remaining
    X-RateLimit-Reset
    

    Ces en-têtes permettent aux applications clientes de connaître le nombre de requêtes restantes avant d’atteindre la limite maximale.

    v0.5.0

    Fonctions d’ancrage configurables qui s’exécutent lorsque la limite est dépassée, utiles pour des tâches telles que :

    • Le journalage
    • La collecte de métriques
    • L’envoi d’alertes
    • Les analyses
    • L’envoi de données vers des outils de surveillance externes

    Pourquoi c’est open source

    Ce projet n’est pas une réponse au manque de solutions existantes — il existe déjà plusieurs excellentes solutions de limitation de débit dans l’écosystème Node. req-guard-lite a été créé parce que l’objectif était un package qui soit :

    • Assez compact pour être lu et compris en une seule fois
    • Simple à étendre
    • Développé en priorité avec TypeScript
    • Exempt de complexités inutiles
    • Assez flexible pour s’adapter aux besoins réels en production

    Le développement de ce projet a également été une excellente opportunité d’apprendre à publier des paquets, à concevoir des API, à écrire des tests, à s’intégrer avec Redis et à créer des abstractions faciles à utiliser par d’autres développeurs.

    Remarques finales

    Ouvrir le code source d’un projet est l’une des façons les plus efficaces de perfectionner ses compétences en ingénierie. Lorsque d’autres développeurs peuvent installer, utiliser et contribuer à votre code, vous êtes contraint de penser au-delà de votre propre cas d’usage immédiat — la documentation, la conception des API, les tests, la gestion des versions et la compatibilité vers l’arrière deviennent tous des contraintes réelles auxquelles vous devez faire face.

    req-guard-lite est né d’un petit middleware conçu pour répondre à un besoin personnel, mais on espère qu’il deviendra une solution utile, légère et extensible pour la limitation des requêtes destinée aux autres développeurs d’Express. Les retours, les suggestions de fonctionnalités et les contributions sont tous les bienvenus.

    Lectures complémentaires