@ikrbasak/rate-limiter
v0.2.1
Published
Lightweight, pluggable, and production-ready rate limiter for Node.js. Supports fine-grained control over rate limiting strategies (fixed window, sliding window, token bucket).
Maintainers
Readme
Rate Limiter (@ikrbasak/rate-limiter)
A flexible, Redis-backed rate limiter for Node.js applications supporting multiple algorithms: Fixed Window, Sliding Window, and Token Bucket.
Features
- 🚀 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
Installation
npm install @ikrbasak/rate-limiter ioredisyarn add @ikrbasak/rate-limiter ioredispnpm add @ikrbasak/rate-limiter ioredisQuick Start
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.`);
} else {
// Rate limit exceeded
console.log(`Rate limit exceeded. Retry after ${result.retryAfter} seconds.`);
}Algorithms
1. Fixed Window
Divides time into fixed intervals and counts requests within each interval.
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
2. Sliding Window
Maintains a log of request timestamps and counts requests within a sliding time window.
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
3. Token Bucket
Maintains a bucket of tokens that are consumed by requests and refilled at a constant rate.
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
Configuration Options
Common Options
type CommonRateLimiterOptions = {
key: string | ValueGenerator<string>; // Static string or function that returns string
window: number; // Time window in milliseconds (positive integer)
limit: number | ValueGenerator<number>; // Static number or function that returns number
};Algorithm-Specific Options
Fixed Window & Sliding Window
{
algo: 'fixed_window' | 'sliding_window',
key: string | (() => string) | (() => Promise<string>),
limit: number | (() => number) | (() => Promise<number>)
}Token Bucket
{
algo: 'token_bucket',
key: string | (() => string) | (() => Promise<string>),
limit: number | (() => number) | (() => Promise<number>), // Maximum tokens
refill: number // Refill rate per second (positive number)
}Advanced Usage
Dynamic Keys
Use functions for dynamic rate limiting:
// Synchronous key generation
const limiter = rateLimiter.create({
algo: 'sliding_window',
key: () => `user:${getCurrentUserId()}:${getClientIP()}`,
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,
});Dynamic Limits
Adjust limits based on user tiers, time of day, or other factors:
// 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;
},
});Multiple Rate Limiters
Combine different algorithms for comprehensive protection:
// 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;Response Format
The evaluate() method returns a RateLimiterEvaluationResult:
{
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)
}Best Practices
1. Choose the Right Algorithm
- 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
2. Key Design
// Good: Specific and meaningful keys
key: () => `api:${endpoint}:user:${userId}`;
key: () => `download:${fileId}:ip:${clientIP}`;
key: async (id: number) => `tier:${await getUserTier(number)}:${userId}`;
// Avoid: Too generic or static when dynamic is needed
key: 'user';
key: 'api';3. Window Sizing
// 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 quotasRedis Configuration
For optimal performance, configure Redis with:
# Redis configuration for rate limiting
maxmemory-policy allkeys-lru
tcp-keepalive 60
timeout 300Performance Considerations
- 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
TypeScript Support
The library is written in TypeScript and provides full type safety:
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();Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feat/amazing-feature) - Commit your changes (
git commit -m 'feat: some amazing feature') - Push to the branch (
git push origin feat/amazing-feature) - Open a Pull Request
License
MIT License. See LICENSE file for details.
Support
- 📧 Email: [email protected]
Keywords: rate limiting, Redis, TypeScript, Node.js, API, throttling, token bucket, sliding window, fixed window, dynamic configuration
