Skip to content

Rate Limiting

KickJS includes a built-in rate limiting middleware that protects your API from abuse. It uses an in-memory store by default and supports pluggable stores for distributed deployments.

Basic Usage

ts
import { rateLimit } from '@forinda/kickjs-http'

bootstrap({
  modules,
  middleware: [
    rateLimit({ max: 100, windowMs: 60_000 }),
  ],
})

This limits each client to 100 requests per minute based on their IP address.

Options

OptionTypeDefaultDescription
maxnumber100Maximum requests per window
windowMsnumber60_000Window size in milliseconds
messagestring'Too Many Requests'Response message on limit exceeded
statusCodenumber429HTTP status code on limit exceeded
keyGenerator(req) => stringreq.ipFunction to derive the rate limit key
headersbooleantrueSend RateLimit-* response headers
storeRateLimitStoreIn-memoryCustom store for distributed rate limiting
skip(req) => booleanSkip rate limiting for certain requests
skipPathsstring[][]Paths to exclude from rate limiting

Response Headers

When headers is enabled (default), the middleware sets:

  • RateLimit-Limit — maximum allowed requests
  • RateLimit-Remaining — remaining requests in the current window
  • RateLimit-Reset — Unix timestamp when the window resets

Per-Route Rate Limiting

Apply different limits to specific routes using the @Middleware decorator:

ts
import { rateLimit } from '@forinda/kickjs-http'

@Controller('/auth')
class AuthController {
  @Post('/login')
  @Middleware(rateLimit({ max: 5, windowMs: 15 * 60_000 }))
  async login(ctx: RequestContext) {
    // 5 attempts per 15 minutes
  }
}

Skip Paths

Exclude health checks or public endpoints:

ts
rateLimit({
  max: 100,
  skipPaths: ['/health', '/metrics'],
})

Custom Key Generator

Rate limit by API key instead of IP:

ts
rateLimit({
  keyGenerator: (req) => req.headers['x-api-key'] as string ?? req.ip ?? '127.0.0.1',
})

Custom Store (Redis)

Implement the RateLimitStore interface for distributed deployments:

ts
import type { RateLimitStore } from '@forinda/kickjs-http'

class RedisStore implements RateLimitStore {
  constructor(private redis: Redis, private windowMs: number) {}

  async increment(key: string) {
    const hits = await this.redis.incr(`rl:${key}`)
    if (hits === 1) await this.redis.pexpire(`rl:${key}`, this.windowMs)
    const ttl = await this.redis.pttl(`rl:${key}`)
    return { totalHits: hits, resetTime: new Date(Date.now() + ttl) }
  }

  async decrement(key: string) {
    await this.redis.decr(`rl:${key}`)
  }

  async reset(key: string) {
    await this.redis.del(`rl:${key}`)
  }
}

rateLimit({ store: new RedisStore(redis, 60_000) })

Released under the MIT License.