req-guard-lite: Ein minimalistischer Rate-Limiter für Express, der TypeScript in den Vordergrund stellt
Erfahren Sie, wie ein leichtgewichtiger Express-Rate-Limiter ohne Abhängigkeiten funktioniert – von den Standardwerten im Arbeitsspeicher über Skalierung mit Redis bis hin zu benutzerdefinierten Schlüsselgeneratoren.
Jede Express-Anwendung erreicht irgendwann den Punkt, an dem eine Rate-Limiting-Funktion erforderlich wird.
Egal, ob das Ziel darin besteht, Anmeldepfade zu schützen, Spam einzudämmen oder versehentliches Übernutzen zu verhindern – das Beschränken des einströmenden Verkehrs verwandelt sich schnell von einem wünschenswerten Feature in eine Notwendigkeit, sobald eine API der Öffentlichkeit zugänglich gemacht wird.
Beim Suchen nach einer Rate-Limiting-Lösung, die eine Reihe gängiger Anforderungen erfüllt, wurde klar, dass es zwar bereits viele leistungsstarke Bibliotheken gibt, viele Projekte jedoch eigentlich nur etwas Kleines, Einfaches und Anpassungsfähiges benötigen.
Genau diese Lücke führte zur Entwicklung von req-guard-lite.
Warum ein weiterer Rate-Limiter?
Die meisten APIs benötigen von Anfang an kein vollständiges Unternehmenssicherheitspaket.
Oft reicht es bereits aus, etwas wie Folgendes schreiben zu können:
app.use(rateLimit({
max: 100,
windowMs: 15 * 60 * 1000
}));
…und wir kehren zum Aufbau des Rests der Anwendung zurück.
Die Entwurfsziele für dieses Paket waren:
- Einfach und leichtgewichtig bleiben
- Schnell einrichtbar sein
- Von Anfang an mit TypeScript entwickelt werden
- Eine einfache Erweiterung ermöglichen
- Sowohl für kleine Projekte als auch für großangelegte Systeme geeignet sein
Einführung in req-guard-lite
req-guard-lite ist ein kompaktes Express-Middleware-Modul, das entwickelt wurde, um Ihre API vor einer überwältigenden Anzahl an Anfragen zu schützen.
Standardmäßig läuft es vollständig im Speicher, kann aber auch durch Anbindung an Redis auf verteilte Architekturen skaliert werden.
Ihr Anwendungsbereich ist absichtlich eng gefasst – es erledigt eine Aufgabe hervorragend:
Es überwacht eingehende Anfragen und lehnt Clients ab, sobald diese die von Ihnen festgelegte Grenze überschreiten.
Funktionen
Leichte Implementierung Keine Laufzeitabhängigkeiten im Kernpaket Funktioniert als Express-Middleware Natives TypeScript-Unterstützung Verfügbar ist die Integration mit Redis Unterstützung für benutzerdefinierte Speicherdienste Unterstützung für benutzerdefinierte Schlüsselgeneratoren
Einführung
Installieren Sie es als Ergänzung zu Express.
npm install req-guard-lite express
Dann verbinden Sie es mit Ihrer Anwendung.
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);
Das ist die gesamte Einrichtung.
Ihre API blockiert nun jeden Client, der innerhalb eines Zeitraums von 15 Minuten mehr als 100 Anfragen sendet.
Standardmäßiger In-Memory-Speicher
Vorab sind die Anfragenzähler im Speicher gespeichert.
Praktisch bedeutet das:
- Kein Redis erforderlich
- Keine Datenbank erforderlich
- Keine zusätzliche Konfiguration nötig
- Ideal für die lokale Entwicklung
- Gute Wahl für die Produktion auf einem einzigen Server
Für den größten Teil der Anwendungen ist das die gesamte Rate-Limiting-Funktion, die man jemals benötigen wird.
Skalierung mit Redis
Sobald eine Anwendung auf mehrere Server oder Container ausgeweitet wird, benötigen diese Instanzen einen gemeinsamen Überblick über die Anzahl der Anfragen.
Darum kümmert sich hier Redis.
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
})
});
Dadurch liest und schreibt jede Serverinstanz die gleichen Zähler, sodass die Limits unabhängig davon konstant bleiben, welcher Knoten eine bestimmte Anfrage bearbeitet.
Kundenspezifische Schlüsselgeneratoren
Rate-Limiting nach IP-Adresse ist nicht immer der richtige Ansatz.
In einigen Fällen ist es sinnvoller, die Limits anhand folgender Elemente festzulegen:
- Eine Benutzer-ID
- Eine API-Schlüssel
- Einen Mieter-Identifikator
- Eine Organisation
- Einen JWT-Subject-Claim
- Oder jeden anderen Identifikator, der zu Ihrem Modell passt
Zur Unterstützung dessen ermöglicht req-guard-lite es Ihnen, Ihre eigene Schlüsselgenerierungsfunktion bereitzustellen.
const limiter = rateLimit({
max: 100,
keyGenerator: (req) =>
req.headers["x-api-key"] as string
});
Oder angepasst an den angemeldeten Benutzer:
const limiter = rateLimit({
max: 50,
keyGenerator: (req) =>
(req as any).user.id
});
Das Middleware-Tool selbst ist unabhängig davon, was der Schlüssel darstellt – es führt lediglich eine Zählung basierend auf dem Identifikator durch, den Ihre Funktion zurückgibt.
Ihre eigene Datenbank verwenden
Erweiterbarkeit war von Anfang an eine zentrale Anforderung.
Anstatt Sie an Redis zu binden, stellt req-guard-lite eine einfache RateLimitStore-Schnittstelle bereit. Wenn Ihre Infrastruktur bereits auf Folgendem beruht:
- PostgreSQL
- DynamoDB
- Memcached
- MongoDB
- SQLite
- Eine andere benutzerdefinierte Caching-Schicht
können Sie diese durch Implementierung dieser einzigen Schnittstelle einbinden.
class MyStore implements RateLimitStore {
consume(key: string) {
// your implementation
}
}
Dieses Design sorgt dafür, dass das Paket an fast jeden bereits laufenden Backend-Anbieter angepasst werden kann.
Ein wichtiger Tipp für die Produktion
Falls Ihre Anwendung hinter folgenden Systemen läuft:
- Nginx
- Einem AWS Load Balancer
- Heroku
- Cloudflare
- Jedem Reverse Proxy
sorgen Sie dafür, dass Express richtig konfiguriert ist:
app.set("trust proxy", 1);
Überspringen Sie diesen Schritt, und Express wird oft jede eingehende Anfrage so behandeln, als käme sie direkt vom Proxy selbst – dadurch teilen alle Benutzer letztendlich denselben Rate-Limit-Bereich. Es handelt sich dabei um eine einzeilige Lösung, die jedoch Probleme in der Produktion verhindert, die viele Teams unvorbereitet erwischen.
Wie es funktioniert
Der interne Ablauf ist absichtlich minimal gehalten:
- Eine Anfrage trifft ein.
- Das Middleware-Modul erzeugt dafür einen Schlüssel (standardmäßig die IP-Adresse des Clients).
- Der aktive Speicher erhöht den Zähler, der mit diesem Schlüssel verknüpft ist.
Weil der Store erweiterbar ist, funktioniert dieser Ablauf unabhängig davon, ob er auf Speicher, Redis oder einer benutzerdefinierten Implementierung basiert.
Warum TypeScript?
Die gesamte Bibliothek wurde in TypeScript geschrieben, was folgende Vorteile bringt:
- Strenge Typisierung überall
- Bessere Autocompletion-Funktionen im Editor
- Einfachere Langzeitwartung
- APIs, die schwieriger falsch verwendet werden können
TypScript-Nutzer erhalten vollständige Typdefinitionen direkt aus der Box, ohne dass zusätzliche @types-Pakete installiert werden müssen.
Roadmap
Die Entwicklung ist weiterhin im Gange, und bereits einige Funktionen sind geplant.
v0.4.0
- Unterstützung für standardmäßige Rate-Limit-Response-Header
X-RateLimit-Limit
X-RateLimit-Remaining
X-RateLimit-Reset
Diese Header geben Client-Anwendungen Aufschluss darüber, wie viele Anfragen noch möglich sind, bevor die Obergrenze erreicht wird.
v0.5.0
Einstellbare Hooks, die ausgelöst werden, wenn eine Grenze überschritten wird – nützlich für Dinge wie:
- Protokollierung
- Sammlung von Metriken
- Alarmierung
- Analysen
- Sendung von Daten an externe Überwachungstools
Warum es Open Source ist
Dieses Projekt entstand nicht als Reaktion darauf, dass bestehende Bibliotheken unzureichend sind – es gibt bereits mehrere hervorragende Lösungen für das Rate-Limiting im Node-Ecosystem. req-guard-lite existiert, weil das Ziel ein Paket war, das:
- so kompakt ist, dass es auf einen Blick gelesen und verstanden werden kann
- einfach erweiterbar ist
- in erster Linie für TypeScript konzipiert wurde
- keine unnötige Komplexität aufweist
- flexibel genug ist, um mit den tatsächlichen Anforderungen in der Produktion mitzuwachsen
Die Entwicklung war zudem eine wertvolle Übung im Veröffentlichen von Paketen, Entwerfen von APIs, Schreiben von Tests, Integration mit Redis sowie in der Gestaltung solcher Abstraktionen, die für andere Entwickler leicht zu nutzen sind.
Fazit
Das Open-Source-Machen eines Projekts ist eine der effektivsten Methoden, um eigene Ingenieursfähigkeiten zu schärfen. Sobald andere Entwickler Ihren Code installieren, nutzen und dazu beitragen können, sind Sie gezwungen, über Ihren eigenen unmittelbaren Anwendungsfall hinauszudenken – Dokumentation, API-Design, Testing, Versionierung und Rückwärtskompatibilität werden zu echten Einschränkungen, um die Sie sich kümmern müssen.
req-guard-lite entstand ursprünglich als kleines Middleware-Tool, um einen persönlichen Bedarf zu erfüllen, doch es soll sich zu einer nützlichen, leichten und erweiterbaren Option zur Rate-Limiting-Funktion für andere Express-Entwickler entwickeln. Feedback, Vorschläge zu neuen Funktionen sowie Beiträge sind jederzeit willkommen.
Weitere Literatur
- Teilen eines Zod-Schemas über Ihr React-Frontend und Node-Backend — Erfahren Sie, wie ein einzelnes Zod-Schema React-Formulare, API-Antworten, Express-Anfragekörper sowie Umgebungsvariablen validieren kann und gleichzeitig passende TypeScript-Typen erzeugt.
- Zod vs express-validator: Zwei Ansätze für Express-Validierung — Vergleicht die auf Schemata basierende Anfragevalidierung mit Zod mit dem auf Ketten basierenden express-validator-Middleware und behandelt dabei Einrichtung, Fehlerformatierung sowie häufige Fallstricke.