Redis Engine
Distributed rate limiting powered by Redis and atomic Lua scripts.
The Redis engine executes atomic Lua scripts for distributed rate limiting. Each strategy picks the Redis data structure that fits its algorithm:
- Fixed Window uses
GET/SET/INCRcounters with millisecond TTLs. - Sliding Window uses sorted sets (
ZADD,ZCARD,ZRANGE) to track per-request timestamps. - Token Bucket uses hashes (
HMGET/HMSET) to store the token count and last-refill time. - Individual Fixed Window uses two keys per identifier (start + count) with
GET/SET/INCR.
The engine supports redis (node-redis), ioredis, and Bun's native client (Bun >= 1.4), and runs on every supported runtime.
Installation
npm install @ratelock/redis redis
# or with ioredis
npm install @ratelock/redis ioredisQuick Start
With a Connection URL
import { fixedWindow } from '@ratelock/redis'
const limiter = await fixedWindow({
url: 'redis://localhost:6379',
limit: 100,
windowMs: 60_000,
})With an Existing Client (node-redis)
import { createClient } from 'redis'
import { fixedWindow } from '@ratelock/redis'
const redisClient = createClient({ url: 'redis://localhost:6379' })
await redisClient.connect()
const limiter = await fixedWindow({
client: redisClient,
limit: 100,
windowMs: 60_000,
prefix: 'my-app', // Optional key prefix
})With ioredis
import IORedis from 'ioredis'
import { fixedWindow } from '@ratelock/redis'
const limiter = await fixedWindow({
client: new IORedis('redis://localhost:6379'),
limit: 100,
windowMs: 60_000,
})With Bun's native client
Zero dependency — pass a client instance or set driver: 'bun':
import { RedisClient } from 'bun'
import { fixedWindow } from '@ratelock/redis'
const limiter = await fixedWindow({
client: new RedisClient('redis://localhost:6379'),
limit: 100,
windowMs: 60_000,
})Configuration Options
All limiters in the Redis engine accept these shared base options:
Prop
Type
Built-in Resilience Policies
All limiters accept optional resilience configurations at creation time:
const limiter = await fixedWindow({
url: 'redis://localhost:6379',
limit: 100,
windowMs: 60_000,
cache: { maxSize: 1000, ttlMs: 30_000 }, // Cache denied results in memory
retry: { maxAttempts: 3 }, // Retry transient Redis failures
circuitBreaker: { failureThreshold: 5, recoveryTimeoutMs: 30_000 },
fallback: 'allow', // Fail-open if Redis is down
})| Option | Type | Description |
|---|---|---|
cache | CacheConfig | Cache denied results in memory to reduce Redis load |
retry | RetryConfig | Retry transient failures with exponential backoff |
circuitBreaker | CircuitBreakerConfig | Stop calling Redis after consecutive failures |
fallback | 'throw' | 'allow' | 'deny' | Behavior when the limiter throws |
All Strategies
import { fixedWindow, slidingWindow, tokenBucket, individualFixedWindow } from '@ratelock/redis'Standalone Policies (Advanced)
For advanced use cases (conditional policies, dynamic composition), import standalone wrappers directly:
import { withCache, withRetry } from '@ratelock/redis'
const limiter = await fixedWindow({ url: 'redis://...', limit: 100, windowMs: 60_000 })
const cached = withCache(limiter, { maxSize: 500, ttlMs: 10_000 })See Resilience Policies for full documentation and when to use standalone vs built-in.
API Reference
The factory functions accept these configuration types:
FixedWindowLimiterConfig
Prop
Type
Valkey Compatibility
RateLock works with Valkey (the Redis-compatible fork). Use @ratelock/redis with standard ioredis or node-redis clients.
Note
Use standard ioredis or node-redis clients. Valkey-specific clients are not tested.
When to Use
- Multi-server or serverless deployments
- When you need consistent rate limits across processes
- High-traffic applications requiring Redis-level performance
- When atomicity matters (no race conditions between checks)
Cleanup
await limiter.destroy() // Closes the Redis connection (if created internally)If you provide your own client, destroy() does not close it - you manage the client lifecycle yourself.
How is this guide?
Last updated on