Skip to content

Rate Limiting

Kaelum v1.7.0 ships a zero-dependency in-memory rate limiter you can toggle with a single line — the same way you enable cors or helmet.

const app = require("kaelum")();
app.setConfig({ rateLimit: true }); // 100 req / 15 min / IP
app.addRoute("/api/data", { get: (req, res) => res.json({ ok: true }) });
app.start(3000);

Pass an object instead of true for granular control:

app.setConfig({
rateLimit: {
windowMs: 60 * 1000, // 1 minute
max: 30, // 30 requests per window
message: "Slow down!",
statusCode: 429, // HTTP status (default)
},
});
OptionTypeDefaultDescription
windowMsnumber900000 (15 min)Sliding window in milliseconds
maxnumber100Max hits per key per window
messagestring | object{ error: "Too many requests…" }Body returned on 429
statusCodenumber429HTTP status when limited
keyGeneratorfunction(req) => req.ipDerive the dedup key from the request
skipfunction() => falseReturn true to bypass limiting
headersbooleantrueSend RateLimit-* / Retry-After headers
storeobjectbuilt-in MemoryStoreCustom store (see below)
app.setConfig({ rateLimit: false });

Removes the middleware and shuts down the cleanup timer.

When headers: true (default), every response includes:

HeaderExampleDescription
RateLimit-Limit100Max requests allowed in the window
RateLimit-Remaining87Requests left in the current window
RateLimit-Reset1711036800Unix epoch (seconds) when window resets
Retry-After42Seconds until the client can retry (only on 429)

Rate-limit by API key instead of IP:

app.setConfig({
rateLimit: {
keyGenerator: (req) => req.headers["x-api-key"] || req.ip,
},
});

Let health checks and webhooks through:

app.setConfig({
rateLimit: {
skip: (req) => req.path === "/health" || req.path.startsWith("/webhooks/"),
},
});

For shared state, provide a custom store that talks to Redis or a similar central store. It must implement three methods:

{
increment(key) → { totalHits: number, resetTime: number }
resetKey(key) → void
shutdown() → void
}