Scaling Guide

Scale hitlimit from a single server to a distributed architecture.

Single Instance

For small deployments, the memory store works well:

import { hitlimit, memoryStore } from '@joint-ops/hitlimit'

const limiter = hitlimit({
  limit: 100,
  window: '1m',
  store: memoryStore({
    cleanupInterval: 60000
  })
})

Horizontal Scaling with Redis

For multiple instances, use Redis to share state:

import { hitlimit } from '@joint-ops/hitlimit'
import { redisStore } from '@joint-ops/hitlimit/stores/redis'

const limiter = hitlimit({
  limit: 1000,
  window: '1m',
  store: redisStore({
    url: 'redis://redis-cluster:6379',
    prefix: 'api:ratelimit:'
  })
})

Redis Cluster Configuration

import { createCluster } from 'redis'
import { redisStore } from '@joint-ops/hitlimit/stores/redis'

const cluster = createCluster({
  rootNodes: [
    { url: 'redis://node1:6379' },
    { url: 'redis://node2:6379' },
    { url: 'redis://node3:6379' }
  ]
})

await cluster.connect()

const limiter = hitlimit({
  limit: 1000,
  window: '1m',
  store: redisStore({ client: cluster })
})

Multi-Tier Rate Limiting

Apply different limits to different routes:

// Global: 1000 requests per minute
const globalLimiter = hitlimit({
  limit: 1000,
  window: '1m',
  store
})

// Strict: 10 requests per minute for auth
const authLimiter = hitlimit({
  limit: 10,
  window: '1m',
  store,
  key: (req) => `auth:${req.ip}`
})

// Relaxed: 5000 requests per minute for reads
const readLimiter = hitlimit({
  limit: 5000,
  window: '1m',
  store,
  key: (req) => `read:${req.ip}`
})

app.use(globalLimiter)
app.use('/auth', authLimiter)
app.use('/api/read', readLimiter)

Sliding Window Algorithm

hitlimit uses a sliding window for accurate rate limiting:

// Fixed window: Resets at exact intervals
// User could make 100 requests at :59 and 100 at :00

// Sliding window: Smooths out bursts
// hitlimit tracks requests across window boundaries
hitlimit({
  limit: 100,
  window: '1m',
  algorithm: 'sliding'  // Default
})

Performance Tips

  • Key design: Keep keys short to reduce memory usage
  • Cleanup: Configure appropriate cleanup intervals
  • Connection pooling: Reuse Redis connections
  • Skip paths: Exclude health checks and static assets
hitlimit({
  limit: 100,
  window: '1m',
  skip: (req) => {
    return req.path === '/health' ||
           req.path.startsWith('/static')
  }
})

Performance Benchmarks

StoreOps/secLatency (p99)
memoryStore500,000+<0.1ms
sqliteStore50,000+<1ms
redisStore100,000+<2ms