@klausrz/rate-limiter
v1.0.0
Published
Redis sliding window rate limiter for Express
Maintainers
Readme
@klausrz/rate-limiter
A Redis-backed sliding window rate limiter for Express. Uses optimistic locking (WATCH / MULTI / EXEC) for accurate, race-free request counting.
Install
npm install @klausrz/rate-limiter ioredisPeer dependencies:
ioredis >= 5andexpress >= 4must be installed in your project.
Usage
Express middleware
import express from "express";
import Redis from "ioredis";
import { rateLimiter } from "@klausrz/rate-limiter";
const app = express();
const redis = new Redis(); // connect to your Redis instance
app.use(
rateLimiter({
redis,
windowMs: 60_000, // 1-minute window
maxRequest: 100, // max 100 requests per window
keyPrefix: "rl:", // optional namespace
})
);The middleware automatically reads the client IP from X-Forwarded-For (first value) and falls back to req.socket.remoteAddress.
Response headers set on every request:
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests allowed in the window |
| X-RateLimit-Remaining | Requests remaining in the current window |
| X-RateLimit-Reset | Unix timestamp (seconds) when the window resets |
| Retry-After | Seconds until retry is allowed (only on 429 responses) |
When the limit is exceeded the middleware responds with HTTP 429:
{ "message": "Too many requests", "retryAfter": 42 }Core function (framework-agnostic)
import Redis from "ioredis";
import { isAllowed } from "@klausrz/rate-limiter";
const redis = new Redis();
const result = await isAllowed("user-123", {
redis,
windowMs: 60_000,
maxRequest: 10,
keyPrefix: "rl:",
});
if (!result.allowed) {
// result.remaining === 0
// result.resetAtMs — timestamp when the oldest request expires
}API
rateLimiter(options) → Express middleware
isAllowed(identifier, options) → Promise<RateLimiterResult>
RateLimiterOption
| Property | Type | Required | Description |
|---|---|---|---|
| redis | Redis | ✓ | ioredis client instance |
| windowMs | number | ✓ | Sliding window length in ms |
| maxRequest | number | ✓ | Max requests per window |
| keyPrefix | string | | Optional Redis key prefix |
RateLimiterResult
| Property | Type | Description |
|---|---|---|
| allowed | boolean | Whether the request is permitted |
| remaining | number | Requests remaining in the window |
| resetAtMs | number | Timestamp (ms) when the window resets |
Algorithm
Uses a sorted set per identifier where each member is the request timestamp. On every call:
- Remove entries older than
now - windowMs(evict stale) - Add the current timestamp
- Check cardinality against
maxRequest - Set key TTL to
windowMsfor automatic cleanup
WATCH / MULTI / EXEC ensures correctness under concurrent requests. On transaction conflict the call retries for up to 2 seconds before failing open.
License
MIT
