Rate Limiting
Pass a rate limit middleware to createServer to apply it globally:
import { createServer } from 'lacis'import { createRateLimit } from 'lacis'
createServer('./routes', { middleware: { beforeRequest: createRateLimit({ windowMs: 60_000, max: 100 }), },})Or scope it to a specific route via a middleware file:
// routes/api/+middleware.tsimport { createRateLimit } from 'lacis'
export const beforeRequest = createRateLimit({ windowMs: 60_000, max: 20 })Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
windowMs | number | 60000 | Time window in milliseconds |
max | number | 100 | Max requests per window per key |
message | string | 'Too Many Requests' | Error message in the 429 response |
keyGenerator | (req) => string | First IP from X-Forwarded-For, or socket.remoteAddress | Function to identify the requester |
Response headers
Section titled “Response headers”Every response includes:
| Header | Description |
|---|---|
X-RateLimit-Limit | The configured max |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets |
When the limit is exceeded, a 429 Too Many Requests response is sent with an additional Retry-After header indicating seconds until the window resets.
Custom key
Section titled “Custom key”By default the rate limiter uses the client’s IP. Use keyGenerator to key by user, API token, or any other identifier:
createRateLimit({ windowMs: 60_000, max: 1000, keyGenerator: (req) => req.getHeader('x-api-key') ?? req.connection.remoteAddress ?? 'unknown',})Distributed store
Section titled “Distributed store”Pass a store option to use any external backend. The store must implement get, set, and delete.
Upstash Redis example:
import { createRateLimit } from 'lacis'import type { RateLimitStore, RateLimitEntry } from 'lacis'import { Redis } from '@upstash/redis'
const redis = new Redis({ url: process.env.UPSTASH_URL!, token: process.env.UPSTASH_TOKEN! })
const store: RateLimitStore = { async get(key) { return await redis.get<RateLimitEntry>(key) ?? undefined }, async set(key, entry) { const ttl = Math.ceil((entry.resetAt - Date.now()) / 1000) await redis.set(key, entry, { ex: ttl }) }, async delete(key) { await redis.del(key) },}
createRateLimit({ windowMs: 60_000, max: 100, store })