@oratis/rate-limit
v1.0.0
Published
Zero-dependency in-memory sliding-window rate limiter for Node. Tiny, fully typed, self-pruning, ESM + CJS.
Maintainers
Readme
@oratis/rate-limit
A zero-dependency, in-memory sliding-window rate limiter for Node. One small file, fully typed, self-pruning so it won't leak memory. Perfect for protecting an API route, a queue worker, or a CLI on a single instance — no Redis required.
- 🪶 Zero dependencies, tiny footprint
- 🎚️ True sliding window (not a coarse fixed bucket)
- 🧹 Self-pruning — an
unref'd timer sweeps stale keys; never keeps your process alive - 🧰 Class API (
peek/reset/dispose) and a one-liner - 📦 Ships ESM + CJS + types
Single-process by design. Behind a load balancer each instance limits independently — fine for coarse abuse protection; use a shared store (Redis) when you need a strict global limit.
Install
npm install @oratis/rate-limitQuick start
import { rateLimit } from "@oratis/rate-limit";
// Allow 100 requests per minute per IP.
const { allowed, remaining, resetMs } = rateLimit(ip, 100, 60_000);
if (!allowed) {
return new Response("Too Many Requests", {
status: 429,
headers: { "Retry-After": String(Math.ceil(resetMs / 1000)) },
});
}Class API
Reach for RateLimiter when you want peek, reset, or explicit lifecycle
control:
import { RateLimiter } from "@oratis/rate-limit";
const limiter = new RateLimiter({ limit: 5, windowMs: 10_000 });
limiter.check("user:42"); // record a request → RateLimitResult
limiter.peek("user:42"); // inspect without recording
limiter.reset("user:42"); // forget one key
limiter.reset(); // forget everything
limiter.dispose(); // stop the prune timer when you're doneRateLimitResult
| Field | Type | Meaning |
| --- | --- | --- |
| allowed | boolean | Whether the request is within the limit. |
| remaining | number | Requests left in the current window (0 when blocked). |
| resetMs | number | Ms until capacity frees up (great for a Retry-After header). |
new RateLimiter(options)
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| limit | number | — | Max requests per rolling window (> 0). |
| windowMs | number | — | Window length in ms (> 0). |
| pruneIntervalMs | number | 300000 | Stale-key sweep interval. 0 disables the timer. |
How it works
Each key keeps the timestamps of its requests. On every check, timestamps
older than windowMs are dropped; if what remains is below limit, the request
is allowed and recorded. This gives a true rolling window — no burst at the
fixed-bucket boundary that naive counters suffer from.
