A generic Redis-backed rate limiting tool built on top of ioredis.
- 🚀 Flexible rate limiting - Control request rates with precision
- 📦 Batch operations - Handle multiple requests at once
- 🎯 Three limiting modes - Binary, N-ary, and Uniform strategies
- ⏱️ Minimum interval control - Enforce delays between requests
- 🔑 Dynamic keys - Use functions to generate keys per user/resource
- ✅ 100% test coverage - Fully tested and reliable
$ npm i ioredis-ratelimit --saveimport Redis from 'ioredis'
import RateLimiter from 'ioredis-ratelimit'
const ratelimiter = RateLimiter({
client: new Redis(),
key: 'my-rate-limiter',
limit: 10, // 10 requests
duration: 1000 // per 1 second
})
// Check rate limit
await ratelimiter() // { total: 1, acknowledged: 1, remaining: 9 }
// Get current status
await ratelimiter.get() // { total: 1, remaining: 9, retryAfterMS: 0 }Consume rate limit quota.
id(optional): Identifier when key is a functiontimes(optional): Number of requests to consume (default: 1)- Returns:
Promise<{ total, acknowledged, remaining }>
Get current rate limit status without consuming quota.
id(optional): Identifier when key is a function- Returns:
Promise<{ total, remaining, retryAfterMS }>
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
client |
Redis |
✅ | - | ioredis client instance |
key |
String|Function |
✅ | - | Rate limiter key or key generator function |
limit |
Number |
✅ | - | Maximum requests allowed in duration |
duration |
Number |
✅ | - | Time window in milliseconds |
difference |
Number |
❌ | 0 |
Minimum milliseconds between requests |
ttl |
Number |
❌ | duration |
Redis key TTL in milliseconds |
mode |
String |
❌ | 'binary' |
Rate limiting mode: 'binary', 'nary', or 'uniform' |
error |
Error |
❌ | Error('Too Many Requests') |
Error thrown when limit exceeded |
import Redis from 'ioredis'
import RateLimiter from 'ioredis-ratelimit'
const ratelimiter = RateLimiter({
client: new Redis(),
key: 'limiter',
limit: 3,
duration: 1000,
difference: 0 // allow no interval between requests
})
;(async () => {
await ratelimiter().then(console.log) // { total: 1, acknowledged: 1, remaining: 2 }
await ratelimiter().then(console.log) // { total: 2, acknowledged: 1, remaining: 1 }
await ratelimiter().then(console.log) // { total: 3, acknowledged: 1, remaining: 0 }
await ratelimiter().then(console.log).catch(console.error) // 429 - Error: Too Many Requests
await ratelimiter.get().then(console.log) // { total: 3, remaining: 0, retryAfterMS: 999 }
})().catch(console.error)import express from 'express'
import Redis from 'ioredis'
import RateLimiter from 'ioredis-ratelimit'
const app = express()
const ratelimiter = RateLimiter({
client: new Redis(),
key: (req) => `limiter:${req.user.id}`,
limit: 10, // 10 requests
duration: 1000, // per 1 second
difference: 10 // minimum 10ms between requests
})
app.use(async (req, res, next) => {
try {
await ratelimiter(req)
next()
} catch (err) {
res.status(429).json({ error: 'Too Many Requests' })
}
})
app.get('/', (req, res) => {
res.json({ message: 'Hello World' })
})
app.listen(3000)const ratelimiter = RateLimiter({
client: new Redis(),
key: 'batch-limiter',
limit: 10,
duration: 1000
})
// Consume 5 requests at once
await ratelimiter(5) // { total: 5, acknowledged: 5, remaining: 5 }const ratelimiter = RateLimiter({
client: new Redis(),
key: (userId) => `ratelimit:user:${userId}`,
limit: 100,
duration: 60000 // 100 requests per minute per user
})
await ratelimiter('user123') // Rate limit for user123
await ratelimiter('user456') // Rate limit for user456 (separate bucket)const ratelimiter = RateLimiter({
client: new Redis(),
key: 'api-calls',
limit: 100,
duration: 60000,
difference: 1000 // Minimum 1s between requests
})
await ratelimiter() // OK
await ratelimiter() // Error: Too Many Requests (called too quickly)All-or-nothing: Either all requests are accepted or all are rejected.
const ratelimiter = RateLimiter({
client: new Redis(),
key: 'limiter',
limit: 5,
duration: 3000,
mode: 'binary'
})
await ratelimiter(2) // { total: 2, acknowledged: 2, remaining: 3 }
await ratelimiter(2) // { total: 4, acknowledged: 2, remaining: 1 }
await ratelimiter(2) // Error: Too Many Requests (needs 2 but only 1 remaining)Use case: Strict rate limiting where partial fulfillment is not acceptable.
Partial acceptance: Accepts as many requests as possible, up to the limit.
const ratelimiter = RateLimiter({
client: new Redis(),
key: 'limiter',
limit: 5,
duration: 3000,
mode: 'nary'
})
await ratelimiter(2) // { total: 2, acknowledged: 2, remaining: 3 }
await ratelimiter(2) // { total: 4, acknowledged: 2, remaining: 1 }
await ratelimiter(2) // { total: 5, acknowledged: 1, remaining: 0 } ✅ Accepts 1 of 2
await ratelimiter(2) // Error: Too Many Requests (bucket full)Use case: Maximize throughput by accepting partial batches.
Lenient: Accepts all requests if there's at least one slot available (can exceed limit).
const ratelimiter = RateLimiter({
client: new Redis(),
key: 'limiter',
limit: 5,
duration: 3000,
mode: 'uniform'
})
await ratelimiter(2) // { total: 2, acknowledged: 2, remaining: 3 }
await ratelimiter(2) // { total: 4, acknowledged: 2, remaining: 1 }
await ratelimiter(2) // { total: 6, acknowledged: 2, remaining: 0 } ✅ Exceeds limit
await ratelimiter(2) // Error: Too Many Requests (no slots available)Use case: Flexible rate limiting where occasional bursts are acceptable.
Ensure Redis is running, then:
$ npm testMIT