Skip to content

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.ts
import { createRateLimit } from 'lacis'
export const beforeRequest = createRateLimit({ windowMs: 60_000, max: 20 })
OptionTypeDefaultDescription
windowMsnumber60000Time window in milliseconds
maxnumber100Max requests per window per key
messagestring'Too Many Requests'Error message in the 429 response
keyGenerator(req) => stringFirst IP from X-Forwarded-For, or socket.remoteAddressFunction to identify the requester

Every response includes:

HeaderDescription
X-RateLimit-LimitThe configured max
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetUnix 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.

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',
})

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 })