Rate limit middleware
Это содержимое пока не доступно на вашем языке.
rateLimit is a per-key request limiter. It returns a
handler wrapper — call it with the options, then call the
returned function with the handler you want to protect.
Excess requests get 429 with a Retry-After header.
import { rateLimit } from 'actor-ts/http';import { InMemoryCache } from 'actor-ts';
const limited = rateLimit({ cache: new InMemoryCache(), windowMs: 60_000, // 1 minute window max: 100, // 100 reqs per key per minute key: (req) => req.headers['x-forwarded-for'] ?? 'unknown',});
const routes = path('api', post(limited(apiHandler)));Each distinct key (here: per X-Forwarded-For IP) gets a 100/minute
budget. Excess requests get 429 with a Retry-After header.
Configuration
Section titled “Configuration”interface RateLimitOptions { cache: Cache; windowMs: number; max: number; key: (req: HttpRequest) => string | Promise<string>; keyPrefix?: string; // default 'rl:' onLimit?: (ctx: RateLimitContext) => HttpResponse;}
interface RateLimitContext { key: string; count: number; max: number; windowMs: number; retryAfterSeconds: number;}| Field | Purpose |
|---|---|
cache | Backing store (in-memory or Redis). |
windowMs | Fixed-window size. |
max | Max requests per key per window. |
key(req) | Derive the rate-limit key — typically client IP, user ID, or API key. Required. |
keyPrefix | Cache-key namespace. Default 'rl:' so multiple limiters in the same Redis don’t collide. |
onLimit(ctx) | Override the 429 response — full control over status / body / headers. |
Custom keying
Section titled “Custom keying”const limited = rateLimit({ cache, windowMs: 60_000, max: 100, key: (req) => { const userId = extractUserId(req); return userId ? `user:${userId}` : `ip:${req.headers['x-forwarded-for'] ?? 'unknown'}`; },});Per-user when authenticated; per-IP when not. Common pattern:
- Auth’d routes — per-user limit.
- Public routes — per-IP limit.
Combine with the framework’s Cache abstraction so the limiter works across pods with Redis backing.
How counting works
Section titled “How counting works”Fixed window (default): - Window starts when the first request in a key arrives. - Increments a counter in the cache. - When counter exceeds `max`, subsequent requests in the window get 429. - TTL on the counter ensures fresh windows start cleanly.The implementation uses the cache’s incr with TTL —
atomic across pods when backed by Redis.
Response headers
Section titled “Response headers”The middleware sets:
X-RateLimit-Limit: 100X-RateLimit-Remaining: 47X-RateLimit-Reset: 1716297600 ← unix timestamp when window resetsRetry-After: 23 ← (on 429 only) seconds until resetClients use these to back off without retrying randomly.
Cluster-aware rate limiting
Section titled “Cluster-aware rate limiting”import { RedisCache } from 'actor-ts';
rateLimit({ cache: new RedisCache({ url: 'redis://...' }), windowMs: 60_000, max: 100, key: (req) => req.headers['x-forwarded-for'] ?? 'unknown',});With Redis-backed cache, all pods share the counter — a client hitting any pod accumulates against the same budget.
With InMemoryCache, each pod has its own counter — a client
distributed across 4 pods gets effectively 4× the limit.
Acceptable for permissive limits; problematic for strict ones.
When to use it
Section titled “When to use it”Three good fits:
- Public APIs — prevent abuse / DoS.
- Tier-based limits — different
keyper tier (free / paid / enterprise). - Per-action limits — login attempts, password resets, email-send rates.
Layered rate limits
Section titled “Layered rate limits”// Different limits per handler:const apiLimited = rateLimit({ cache, windowMs: 60_000, max: 1000, key: (req) => req.headers['x-user-id'] ?? req.headers['x-forwarded-for'] ?? 'anon',});
const loginLimited = rateLimit({ cache, windowMs: 60_000, max: 10, key: (req) => `login:${req.headers['x-forwarded-for'] ?? 'unknown'}`,});
const routes = concat( path('login', post(loginLimited(loginHandler))), path('orders', get(apiLimited(ordersHandler))),);Stricter limits on sensitive handlers; more permissive on general traffic. Compose by wrapping each handler with the matching limiter.
Where to next
Section titled “Where to next”- HTTP overview — the bigger picture.
- Response cache middleware — complementary read-side middleware.
- Idempotency-key middleware — for write-dedup.
- Cache overview — the backing store.