omni-rate-limiter
v1.0.1
Published
A comprehensive, high-performance rate limiting package for Node.js applications with multiple algorithms and storage backends
Maintainers
Readme
Omni Rate Limiter
The rate limiter that doesn't get in your way. Powerful enough for production, simple enough to set up in 60 seconds.
Stop API abuse without the headache. Works with Express, Fastify, Next.js, and any Node.js app. Choose from 6 battle-tested algorithms. Store data in memory, Redis, PostgreSQL, or MongoDB. TypeScript ready. Zero config to get started.
Why Developers Love It
- 🚀 3 lines to production - That's all it takes.
- 🎯 Smart defaults - Works great out of the box, customize when you need to.
- 🔥 Actually fast - Built for high-traffic apps.
- 🛠️ Use what you have - Memory, Redis, Postgres, Mongo - your choice.
- 📦 Batteries included - Express, Fastify, Next.js middleware ready to go.
Installation
npm install omni-rate-limiterQuick Start
Here's how to add rate limiting to your app:
const { createRateLimiter } = require('omni-rate-limiter');
// Create a rate limiter: 100 requests per hour
const limiter = createRateLimiter({
limit: 100,
window: '1h',
algorithm: 'sliding-window-counter' // Optional - this is the default
});
// Use it in your code
async function handleRequest(userId) {
const result = await limiter.check(userId);
if (result.allowed) {
// Request is allowed - process it
console.log(`✓ Request allowed. ${result.remaining} remaining.`);
} else {
// Too many requests - reject it
console.log(`✗ Rate limit hit. Try again in ${result.retryAfter}ms`);
}
}Note: The order of options doesn't matter - they're just object properties. The algorithm is optional and defaults to sliding-window-counter.
Using with Express.js
Add rate limiting to your Express app as middleware:
const express = require('express');
const { expressRateLimit } = require('omni-rate-limiter');
const app = express();
// Example 1: Rate limit by IP address (most common)
app.use(expressRateLimit({
limit: 100,
window: '15m',
keyGenerator: (req) => req.ip, // Each IP gets 100 requests per 15 min
message: 'Too many requests, please slow down'
}));
// Example 2: Rate limit by API key (with IP fallback)
app.use('/api/', expressRateLimit({
limit: 1000,
window: '1h',
keyGenerator: (req) => req.headers['x-api-key'] || req.ip
}));
// Example 3: Rate limit by user ID (for authenticated routes)
app.use('/dashboard/', expressRateLimit({
limit: 500,
window: '1h',
keyGenerator: (req) => req.user?.id || req.ip // User ID if logged in, else IP
}));
app.listen(3000);Understanding keyGenerator
The keyGenerator function tells the rate limiter who to track. Think of it as "what makes users unique?"
Common patterns:
// By IP address - each IP address gets their own limit
keyGenerator: (req) => req.ip
// By user ID - each logged-in user gets their own limit
keyGenerator: (req) => req.user.id
// By API key - each API key gets their own limit
keyGenerator: (req) => req.headers['x-api-key']
// Combination - use API key if present, otherwise IP
keyGenerator: (req) => req.headers['x-api-key'] || req.ip
// Global - everyone shares the same limit (rarely used)
keyGenerator: (req) => 'global'Default behavior: If you don't specify keyGenerator, it defaults to (req) => req.ip || 'global'
Next.js Integration
// middleware.js
import { nextjsRateLimit } from 'omni-rate-limiter';
const rateLimiter = nextjsRateLimit({
limit: 10,
window: '1m',
keyGenerator: (request) => request.ip
});
export async function middleware(request) {
const rateLimitResult = await rateLimiter(request);
if (rateLimitResult) {
return rateLimitResult; // Rate limit exceeded
}
// Continue to the next middleware or page
}
export const config = {
matcher: '/api/:path*'
};Fastify Integration
const fastify = require('fastify')({ logger: true });
const { fastifyRateLimit } = require('omni-rate-limiter');
// Register the rate limiting plugin
fastify.register(fastifyRateLimit, {
limit: 100,
window: '1h',
keyGenerator: (request) => request.ip
});
fastify.listen({ port: 3000 });Choose Your Algorithm
Different algorithms work better for different situations. Here's what each one does:
Token Bucket (Good for APIs with bursts)
Allows sudden bursts of traffic, then refills slowly. Like a bucket that fills with tokens over time.
const limiter = createRateLimiter({
algorithm: 'token-bucket',
limit: 10,
window: '1m'
});Use when: Your API can handle occasional bursts but needs to limit sustained traffic.
Sliding Window (Most accurate)
Tracks every request timestamp precisely. No edge cases or loopholes.
const limiter = createRateLimiter({
algorithm: 'sliding-window',
limit: 100,
window: '1h'
});Use when: You need exact rate limiting and have the memory for it.
Fixed Window (Fastest and simplest)
Resets the counter at fixed intervals. Very efficient but has edge cases.
const limiter = createRateLimiter({
algorithm: 'fixed-window',
limit: 1000,
window: '1h'
});Use when: Performance is critical and you can tolerate some edge cases.
Sliding Window Counter (Recommended for most cases)
Best balance between accuracy and performance. This is the default.
const limiter = createRateLimiter({
algorithm: 'sliding-window-counter',
limit: 100,
window: '1h'
});Use when: You want good accuracy without the memory cost of full sliding window.
Leaky Bucket (Smooth traffic)
Processes requests at a steady rate, smoothing out bursts.
const limiter = createRateLimiter({
algorithm: 'leaky-bucket',
limit: 10,
window: '1m'
});Use when: You want to enforce a steady request rate.
Adaptive (Smart adjustment)
Automatically adjusts limits based on your server's performance.
const limiter = createRateLimiter({
algorithm: 'adaptive',
limit: 100,
window: '1m'
});Use when: Your server load varies and you want automatic adjustment.
Not sure which to pick? Use sliding-window-counter (the default) - it works great for most apps.
Where to Store Rate Limit Data
By default, rate limits are stored in memory (RAM). This works great for single servers, but you can also use Redis, PostgreSQL, or MongoDB.
Memory (Default - No Setup Required)
Just use the rate limiter - it stores everything in RAM automatically.
const limiter = createRateLimiter({
limit: 100,
window: '1h'
// That's it! Uses memory automatically
});Good for: Development, testing, single server apps
Not good for: Multiple servers (data isn't shared)
Redis (Best for Production)
Fast and perfect when you have multiple servers.
Option 1: Individual parameters
const { createRateLimiter, createRedisStorage } = require('omni-rate-limiter');
const storage = createRedisStorage({
host: 'localhost',
port: 6379,
password: 'yourpassword', // Optional: if Redis has auth
db: 0, // Optional: Redis database number (default: 0)
keyPrefix: 'rl:' // Optional: prefix for all keys (default: 'rl:')
});
const limiter = createRateLimiter({
limit: 100,
window: '1h',
storage: storage
});Option 2: Connection string (easier!)
const storage = createRedisStorage({
url: 'redis://username:password@localhost:6379/0',
keyPrefix: 'rl:' // Optional
});Connection string format: redis://[username]:[password]@[host]:[port]/[database]
Redis keys created: rl:your-key-here (e.g., rl:192.168.1.1 for IP-based limiting)
Good for: Production apps, multiple servers, high traffic
PostgreSQL
Use your existing PostgreSQL database. Tables are created automatically!
Option 1: Individual parameters
const { createRateLimiter, createPostgreSQLStorage } = require('omni-rate-limiter');
const storage = createPostgreSQLStorage({
host: 'localhost',
port: 5432,
database: 'myapp',
user: 'postgres',
password: 'mypassword',
tableName: 'rate_limits' // Optional: table name (default: 'rate_limits')
});
const limiter = createRateLimiter({
limit: 100,
window: '1h',
storage: storage
});Option 2: Connection string (easier!)
const storage = createPostgreSQLStorage({
connectionString: 'postgresql://postgres:mypassword@localhost:5432/myapp',
tableName: 'rate_limits' // Optional
});Connection string format: postgresql://[user]:[password]@[host]:[port]/[database]
Table created: rate_limits with columns: key, value, expires_at, created_at, updated_at
Indexes: Automatically created on key and expires_at for performance
Good for: Apps already using PostgreSQL
MongoDB
Works great with MongoDB's automatic TTL cleanup.
Simple connection string (recommended)
const { createRateLimiter, createMongoDBStorage } = require('omni-rate-limiter');
const storage = createMongoDBStorage({
url: 'mongodb://localhost:27017',
database: 'myapp', // Optional: database name (default: 'rate_limiter')
collection: 'rate_limits' // Optional: collection name (default: 'rate_limits')
});
const limiter = createRateLimiter({
limit: 100,
window: '1h',
storage: storage
});With authentication:
const storage = createMongoDBStorage({
url: 'mongodb://username:password@localhost:27017',
database: 'myapp'
});MongoDB Atlas (cloud):
const storage = createMongoDBStorage({
url: 'mongodb+srv://username:[email protected]',
database: 'myapp'
});Connection string format: mongodb://[username]:[password]@[host]:[port] or mongodb+srv://[username]:[password]@[cluster]
Collection created: rate_limits with fields: _id (key), value, expiresAt, updatedAt
TTL Index: Automatically created on expiresAt - MongoDB cleans up expired data for you!
Good for: Apps already using MongoDB
Advanced Options
Rate Limit by User Instead of IP
const limiter = createRateLimiter({
limit: 100,
window: '1h',
keyGenerator: (req) => {
// Use user ID if logged in, otherwise use IP
return req.user?.id || req.ip;
}
});Don't Count Failed Requests
const limiter = createRateLimiter({
limit: 100,
window: '1h',
skipFailedRequests: true // Only count successful requests
});Custom Error Messages
const middleware = expressRateLimit({
limit: 100,
window: '1h',
message: 'Whoa there! You are making too many requests. Take a break.',
onLimitReached: (key, result) => {
console.log(`User ${key} hit the rate limit`);
}
});Metrics and Monitoring
const { createRateLimiter, createMetricsCollector } = require('omni-rate-limiter');
const metrics = createMetricsCollector();
const limiter = createRateLimiter({
limit: 100,
window: '1h',
onLimitReached: (key, result) => {
metrics.recordRequest(key, false, Date.now() - result.resetTime);
}
});
// Get metrics
setInterval(() => {
const stats = metrics.getMetrics();
console.log('Rate limiter stats:', stats);
}, 60000);Error Handling
const { createRateLimiter, createRateLimiterError } = require('omni-rate-limiter');
try {
const result = await limiter.check('user123');
// Handle result
} catch (error) {
if (error.name === 'RateLimiterError') {
switch (error.code) {
case 'STORAGE_ERROR':
console.error('Storage backend error:', error.originalError);
break;
case 'CONFIGURATION_ERROR':
console.error('Configuration error:', error.message);
break;
default:
console.error('Rate limiter error:', error.message);
}
}
}Performance Tips
Algorithm choice matters:
- Most apps: Use
sliding-window-counter(default) - Need speed: Use
fixed-window - Need precision: Use
sliding-window
Storage matters too:
- Single server: Use memory (default)
- Multiple servers: Use Redis
- Already have a database: Use PostgreSQL or MongoDB
Keep keys simple:
- Short keys = better performance
- Don't create millions of unique keys
Set appropriate limits:
- Too low = frustrated users
- Too high = no protection
- Start conservative, adjust based on monitoring
API Reference
Main Function
createRateLimiter(options)
Creates a rate limiter.
Options:
algorithm- Which algorithm to use (default:'sliding-window-counter')limit- How many requests allowed (default:100)window- Time period (default:'1h'). Examples:'1m','30s','2h'storage- Where to store data (default: memory)keyGenerator- Function to generate keys from requestsskipFailedRequests- Don't count failed requests (default:false)onLimitReached- Callback when limit is hit
Returns an object with:
check(key)- Check if request is allowedreset(key)- Reset the limit for a keygetInfo(key)- Get current status
Storage Functions
createRedisStorage(options)- Use RediscreatePostgreSQLStorage(options)- Use PostgreSQLcreateMongoDBStorage(options)- Use MongoDBcreateCustomStorage(implementation)- Use your own storage
Middleware
expressRateLimit(options)- Express middlewarefastifyRateLimit(options)- Fastify pluginnextjsRateLimit(options)- Next.js middlewarenextjsApiRateLimit(options)- Next.js API routes
Examples and Demos
Check out the Express demo folder:
- Complete Express server with rate limiting
- All algorithms demonstrated with working endpoints
- Database setup guides (Redis, PostgreSQL, MongoDB)
- Connection string examples with authentication
- Ready to run - just cd node_modules/omni-rate-limiter/expressjs-demo folder and
npm install && npm start
The demo is the fastest way to understand how everything works!
Contributing
Found a bug or want to add a feature? Please email at [email protected] .
License
MIT - Use it however you want!
