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
| Store | Ops/sec | Latency (p99) |
|---|---|---|
| memoryStore | 500,000+ | <0.1ms |
| sqliteStore | 50,000+ | <1ms |
| redisStore | 100,000+ | <2ms |