Lightweight, pluggable, and production-ready rate limiter for Node.js. Supports fine-grained control over rate limiting strategies (fixed window, sliding window, token bucket).
npm install @ikrbasak/rate-limiterA flexible, Redis-backed rate limiter for Node.js applications supporting multiple algorithms: Fixed Window, Sliding Window, and Token Bucket.
- 🚀 Multiple Algorithms: Fixed Window, Sliding Window, and Token Bucket
- 🔄 Redis-backed: Distributed rate limiting across multiple instances
- 📝 TypeScript Support: Full type safety with comprehensive JSDoc documentation
- ⚡ High Performance: Optimized Redis operations with pipelining
- 🎯 Dynamic Configuration: Support for dynamic keys and limits using sync/async functions
- 🛡️ Production Ready: Battle-tested algorithms with proper error handling
``bash`
npm install @ikrbasak/rate-limiter ioredis
`bash`
yarn add @ikrbasak/rate-limiter ioredis
`bash`
pnpm add @ikrbasak/rate-limiter ioredis
`typescript
import Redis from 'ioredis';
import RateLimiter from '@ikrbasak/rate-limiter';
// Initialize Redis connection
const redis = new Redis('redis://localhost:6379');
// Create rate limiter instance
const rateLimiter = new RateLimiter(redis, {
prefix: 'api', // Optional: custom Redis key prefix, defaults to 'rl'
});
// Create a rate limiter
const limiter = rateLimiter.create({
algo: 'fixed_window',
key: 'user:123',
window: 60000, // 1 minute in milliseconds
limit: 100, // 100 requests per minute
});
// Check rate limit
const result = await limiter.evaluate();
if (result.allowed) {
// Process the request
console.log(Request allowed. ${result.remaining} requests remaining.);Rate limit exceeded. Retry after ${result.retryAfter} seconds.
} else {
// Rate limit exceeded
console.log();`
}
Divides time into fixed intervals and counts requests within each interval.
`typescript`
const limiter = rateLimiter.create({
algo: 'fixed_window',
key: 'api:user:123',
window: 60000, // 1 minute window
limit: 100, // 100 requests per window
});
Use cases:
- Simple API rate limiting
- Resource quotas
- Basic request throttling
Pros: Simple, memory efficient
Cons: Potential burst at window boundaries
Maintains a log of request timestamps and counts requests within a sliding time window.
`typescript`
const limiter = rateLimiter.create({
algo: 'sliding_window',
key: 'api:user:123',
window: 60000, // 1 minute sliding window
limit: 100, // 100 requests per minute
});
Use cases:
- More accurate rate limiting
- Preventing burst attacks
- Fair usage enforcement
Pros: Accurate, smooth rate limiting
Cons: Higher memory usage
Maintains a bucket of tokens that are consumed by requests and refilled at a constant rate.
`typescript`
const limiter = rateLimiter.create({
algo: 'token_bucket',
key: 'api:user:123',
window: 3600000, // 1 hour token bucket lifetime
limit: 1000, // Maximum 1000 tokens
refill: 10, // 10 tokens per second
});
Use cases:
- Allowing burst traffic
- Credit-based systems
- Flexible rate limiting
Pros: Allows bursts, flexible
Cons: More complex to understand
`typescript`
type CommonRateLimiterOptions = {
key: string | ValueGenerator
window: number; // Time window in milliseconds (positive integer)
limit: number | ValueGenerator
};
#### Fixed Window & Sliding Window
`typescript`
{
algo: 'fixed_window' | 'sliding_window',
key: string | (() => string) | (() => Promise
limit: number | (() => number) | (() => Promise
}
#### Token Bucket
`typescript`
{
algo: 'token_bucket',
key: string | (() => string) | (() => Promise
limit: number | (() => number) | (() => Promise
refill: number // Refill rate per second (positive number)
}
Use functions for dynamic rate limiting:
`typescriptuser:${getCurrentUserId()}:${getClientIP()}
// Synchronous key generation
const limiter = rateLimiter.create({
algo: 'sliding_window',
key: () => ,
window: 60000,
limit: 50,
});
// Asynchronous key generation
const asyncLimiter = rateLimiter.create({
algo: 'token_bucket',
key: async (id: number) => {
const userId = await getUserId(id);
const tier = await getUserTier(userId);
return ${tier}:${userId};`
},
window: 3600000,
limit: 1000,
refill: 1,
});
Adjust limits based on user tiers, time of day, or other factors:
`typescript
// Synchronous limit generation
const limiter = rateLimiter.create({
algo: 'fixed_window',
key: 'api:user:123',
window: 60000,
limit: () => {
const hour = new Date().getHours();
return hour >= 9 && hour <= 17 ? 1000 : 100; // Higher limits during business hours
},
});
// Asynchronous limit generation
const premiumLimiter = rateLimiter.create({
algo: 'sliding_window',
key: 'api:user:123',
window: 60000,
limit: async () => {
const user = await getUserById(123);
return user.tier === 'premium' ? 1000 : 100;
},
});
`
Combine different algorithms for comprehensive protection:
`typescript
// Burst protection (short-term)
const burstLimiter = rateLimiter.create({
algo: 'fixed_window',
key: 'api:user:123',
window: 1000, // 1 second
limit: 10,
});
// Sustained usage (long-term)
const sustainedLimiter = rateLimiter.create({
algo: 'sliding_window',
key: 'api:user:123',
window: 3600000, // 1 hour
limit: 1000,
});
// Check both limits
const [burstResult, sustainedResult] = await Promise.all([
burstLimiter.evaluate(),
sustainedLimiter.evaluate(),
]);
const allowed = burstResult.allowed && sustainedResult.allowed;
`
The evaluate() method returns a RateLimiterEvaluationResult:
`typescript`
{
limit: number; // Applied limit (positive integer)
allowed: boolean; // Whether request is allowed
remaining: number; // Requests remaining (non-negative integer)
retryAfter: number; // Seconds to wait before retry (0 if allowed)
}
- Fixed Window: Simple APIs, basic protection, memory efficient
- Sliding Window: Accurate limiting, prevent bursts, fair usage
- Token Bucket: Allow legitimate bursts, credit systems, flexible limits
`typescriptapi:${endpoint}:user:${userId}
// Good: Specific and meaningful keys
key: () => ;download:${fileId}:ip:${clientIP}
key: () => ;tier:${await getUserTier(number)}:${userId}
key: async (id: number) => ;
// Avoid: Too generic or static when dynamic is needed
key: 'user';
key: 'api';
`
`typescript`
// Multiple time windows for different scenarios
const shortTerm = { window: 1000, limit: 5 }; // Burst protection
const mediumTerm = { window: 60000, limit: 100 }; // Per-minute limits
const longTerm = { window: 3600000, limit: 1000 }; // Hourly quotas
For optimal performance, configure Redis with:
`redis`Redis configuration for rate limiting
maxmemory-policy allkeys-lru
tcp-keepalive 60
timeout 300
- Connection Pooling: Use Redis connection pooling for high-traffic applications
- Key Expiration: All keys are automatically set to expire to prevent memory leaks
- Pipeline Operations: The library uses Redis transactions for atomic operations
- Memory Usage: Sliding window uses more memory than fixed window due to timestamp storage
- Dynamic Function Caching: Cache results of expensive async operations when possible
The library is written in TypeScript and provides full type safety:
`typescript
import RateLimiter, {
RateLimiterOptions,
RateLimiterEvaluationResult,
ValueGenerator,
FixedWindowRateLimiterOptions,
SlidingWindowRateLimiterOptions,
TokenBucketRateLimiterOptions,
} from '@ikrbasak/rate-limiter';
// Type-safe configuration
const options: FixedWindowRateLimiterOptions = {
algo: 'fixed_window',
key: 'user:123',
window: 60000,
limit: 100,
};
// Type-safe result handling
const result: RateLimiterEvaluationResult = await limiter.evaluate();
`
1. Fork the repository
2. Create your feature branch (git checkout -b feat/amazing-feature)git commit -m 'feat: some amazing feature'
3. Commit your changes ()git push origin feat/amazing-feature`)
4. Push to the branch (
5. Open a Pull Request
MIT License. See LICENSE file for details.
- 📧 Email: ikrbasak@gmail.com
---
Keywords: rate limiting, Redis, TypeScript, Node.js, API, throttling, token bucket, sliding window, fixed window, dynamic configuration