Configuration

Table of Contents

Quick Start

const { RateLimiter } = require('flex-rate-limit');

const limiter = new RateLimiter({
  windowMs: 60 * 1000,
  max: 100,
  algorithm: 'sliding-window',
});

The two required ideas are the time window and the maximum number of allowed requests. Other options refine the algorithm, storage backend, key generation, middleware behavior, and route overrides.

Core Options

OptionTypeDefaultDescription
windowMsnumber60000Time window in milliseconds
maxnumber | Function100Maximum requests per window
algorithmstringsliding-windowRate limiting algorithm
storeStore | 'memory' | 'redis://...'memoryStorage backend
keyGeneratorFunctionIP-basedBuilds a key from request context
skipFunction() => falseBypasses rate limiting when true
handlerFunctionbuilt-in 429 responseCustom over-limit response
headersbooleantrueWrites rate-limit headers
perRouteObjectnullRoute-level overrides
skipSuccessfulRequestsbooleanfalseRoll back successful responses
skipFailedRequestsbooleanfalseRoll back failed responses

Algorithm

algorithm: 'sliding-window'
algorithm: 'fixed-window'
algorithm: 'token-bucket'
algorithm: 'leaky-bucket'
AlgorithmFairnessState CostTypical UseThroughput
sliding-windowHighestHigherAPI limits, login protectionMedium
fixed-windowLowerLowVery hot coarse limitsHigh
token-bucketMedium-highLowAPI gateways, burst-friendly plansMedium-high
leaky-bucketMedium-highLowTraffic shaping, backend protectionMedium-high

1. Sliding Window

Use sliding window when you need the most precise rolling-window behavior.

const loginLimiter = new RateLimiter({
  windowMs: 15 * 60 * 1000,
  max: 5,
  algorithm: 'sliding-window',
});

Recommended for:

  • Login and authentication flows
  • Payment or sensitive actions
  • Per-user API limits where fairness matters

2. Fixed Window

Use fixed window when a coarse window is acceptable and you want a simple hot path.

const publicLimiter = new RateLimiter({
  windowMs: 60 * 1000,
  max: 1000,
  algorithm: 'fixed-window',
});

Fixed-window counters reset at window boundaries, so requests can cluster around the boundary.

3. Token Bucket

Use token bucket when controlled bursts are acceptable and capacity refills over time.

const apiLimiter = new RateLimiter({
  algorithm: 'token-bucket',
  windowMs: 60 * 1000,
  max: 100,
  capacity: 100,
  refillRate: 100,
});

Compared with sliding window:

DimensionSliding WindowToken Bucket
FairnessStrict rolling windowBurst-friendly
After quota is usedWait for old requests to expireTokens refill gradually
Best fitLogin and sensitive APIsAPI plans and gateways

4. Leaky Bucket

Use leaky bucket when the goal is to smooth traffic to a backend.

const backendLimiter = new RateLimiter({
  algorithm: 'leaky-bucket',
  windowMs: 60 * 1000,
  max: 100,
  capacity: 100,
  leakRate: 100,
});

Leaky bucket allows requests while the bucket has capacity and gradually drains state over time.

Store

Memory

const limiter = new RateLimiter({
  store: 'memory',
});

Use Memory for single-process services, tests, local development, and low-complexity deployments.

Redis Connection String

const limiter = new RateLimiter({
  store: 'redis://localhost:6379',
});

await limiter.close();

The library creates and owns the Redis client in this form. Short-lived scripts and services should call await limiter.close().

RedisStore Instance

const Redis = require('ioredis');
const { RateLimiter, RedisStore } = require('flex-rate-limit');

const redis = new Redis('redis://localhost:6379');

const limiter = new RateLimiter({
  store: new RedisStore({ client: redis, ownsClient: false }),
});

The caller owns the Redis client by default. Set ownsClient: true if the store should close it.

CacheHubStore Atomic Backend

const Redis = require('ioredis');
const { RateLimiter, CacheHubStore } = require('flex-rate-limit');

const redis = new Redis('redis://localhost:6379');

const limiter = new RateLimiter({
  store: new CacheHubStore({ client: redis, prefix: 'rl:' }),
});

Use CacheHubStore when you want cache-hub atomic state primitives while keeping flex-rate-limit as the algorithm and middleware layer.

Key Generator

The key generator controls the isolation boundary of a quota.

const limiter = new RateLimiter({
  keyGenerator: (req) => `user:${req.user?.id || 'guest'}:${req.path}`,
});

Common key strategies:

StrategyExample KeyUse
IP onlyip:203.0.113.10Anonymous public traffic
User IDuser:42Authenticated APIs
User + routeuser:42:/api/payBusiness locks and route-level fairness
Tenant + actiontenant:acme:invoice:createMulti-tenant SaaS
API keyapi-key:abc123Gateway-style plans

Middleware Behavior

skip

const limiter = new RateLimiter({
  skip: (req) => req.path === '/health',
});

Use skip for routes that should not be counted, such as health checks. Do not use skip to make allowlisted IPs bypass limits unless that is explicitly intended.

Custom Handler

const limiter = new RateLimiter({
  handler: (req, res) => {
    res.status(429).json({
      error: 'Too Many Requests',
    });
  },
});

Response Outcome Rollback

const limiter = new RateLimiter({
  skipSuccessfulRequests: false,
  skipFailedRequests: true,
});

skipSuccessfulRequests and skipFailedRequests use rollback metadata internally. Public check() results do not expose that metadata unless trackRollback: true is requested.

Route-Level Configuration

const limiter = new RateLimiter({
  windowMs: 60 * 1000,
  max: 100,
  perRoute: {
    '/api/login': { max: 5, windowMs: 15 * 60 * 1000 },
    '/api/users': { max: 100, windowMs: 60 * 1000 },
    '/api/admin': { max: 20, windowMs: 60 * 1000 },
  },
});

Route-level overrides are useful when the same application needs different limits for login, query, admin, and write operations.

Dynamic Limits

max can be a function:

const limiter = new RateLimiter({
  max: (req) => {
    if (req.user?.plan === 'enterprise') return 5000;
    if (req.user?.plan === 'pro') return 1000;
    return 100;
  },
});

Use dynamic limits for plan-based quotas, tenant-specific limits, or internal service accounts.

Scenario Presets

ScenarioAlgorithmWindowMaxNotes
Loginsliding-window15 minutes5-10Include username or user ID in the key
Public APItoken-bucket1 minute100-1000Allows controlled bursts
Sensitive writessliding-window1 hour20-100Pair with business keys
Internal health checksfixed-window1 minutehighOften skipped or lightly limited
Backend protectionleaky-bucket1 minutebackend capacitySmooths request flow

Redis Configuration

const Redis = require('ioredis');
const { RateLimiter, RedisStore } = require('flex-rate-limit');

const redis = new Redis({
  host: '127.0.0.1',
  port: 6379,
  db: 0,
});

const limiter = new RateLimiter({
  windowMs: 60 * 1000,
  max: 100,
  store: new RedisStore({
    client: redis,
    prefix: 'rl:',
  }),
});

For cluster and sentinel examples, see Storage Backends.