ratewarden
v1.1.0
Published
A zero-config, identity-aware, tier-based rate limiter for Node.js APIs
Downloads
322
Maintainers
Readme
Ratewarden
A zero-config, identity-aware, tier-based rate limiter for Node.js APIs
Stop API abuse without the hassle. ratewarden automatically understands who is making requests and applies intelligent rate limits based on user tiers.
Why Ratewarden?
Most rate limiters are either:
- Too simple: Only rate limit by IP (breaks for shared networks)
- Too complex: Require Redis, complex configuration, or 20+ options
ratewarden is the Goldilocks solution:
- Zero-config: Works out of the box with sensible defaults
- Identity-aware: Automatically detects users from tokens, headers, or IP
- Tier-based: Different limits for free, pro, admin users
- Standard headers: Uses draft IETF RateLimit headers
- No dependencies: Pure Node.js, no Redis required (but Redis supported for distributed systems)
- Production-ready: Memory-safe with automatic cleanup
- Scales up: Optional Redis support for multi-server deployments
Quick Start
Installation
npm install ratewardenBasic Usage (Zero Config)
const express = require('express');
const ratewarden = require('ratewarden');
const app = express();
// That's it! One line of code.
app.use(ratewarden());
app.get('/api/data', (req, res) => {
res.json({ message: 'Success!' });
});
app.listen(3000);Default limits:
- Authenticated users (token/user ID): 60 requests / minute
- Anonymous users (IP only): 30 requests / minute
How It Works
Smart Identity Resolution
ratewarden automatically figures out WHO is making the request:
Request → Check Authorization header (JWT/API key)
→ Check x-user-id header
→ Fall back to IP addressThis means:
- Users with auth tokens get their own limits
- Shared WiFi (coffee shops, offices) doesn't break your API
- Anonymous users still get protected
Automatic Tier Assignment
Based on identity, users are automatically assigned tiers:
| Identity Source | Default Tier | Limit |
|----------------|--------------|-------|
| Authorization token | free | 60/min |
| x-user-id header | free | 60/min |
| IP address only | guest | 30/min |
Advanced Usage
Custom Tiers
app.use(ratewarden({
windowMs: 60000, // 1 minute
tiers: {
free: 100,
pro: 1000,
admin: Infinity
}
}));Custom Tier Resolution
Map request to tier based on user data:
app.use(ratewarden({
resolveTier: (req) => {
// Assuming you have auth middleware that sets req.user
if (req.user?.plan === 'premium') return 'pro';
if (req.user?.isAdmin) return 'admin';
return 'free';
}
}));Custom Identity Key
app.use(ratewarden({
keyGenerator: (req) => {
// Rate limit by organization ID instead of user
return req.user?.organizationId || req.ip;
}
}));Custom Error Handling
app.use(ratewarden({
onLimitReached: (req, res, info) => {
console.log(`Rate limit hit: ${info.tier}, ${info.current}/${info.limit}`);
// Still sends 429, but you can log/alert
}
}));Route-Specific Limits
// Strict limits on auth endpoints
app.use('/auth', ratewarden({
tiers: { guest: 5 },
windowMs: 60000
}));
// Relaxed limits on public API
app.use('/api', ratewarden({
tiers: { guest: 100 },
windowMs: 60000
}));Using with Redis (Distributed Systems)
For multi-server deployments, use Redis to share rate limit state across all servers:
Installation
npm install redisBasic Setup
const { createClient } = require('redis');
const ratewarden = require('ratewarden');
// Create and connect Redis client
const redisClient = await createClient({
url: 'redis://localhost:6379'
}).connect();
// Pass redisClient to ratewarden
app.use(ratewarden({
redisClient
}));Full Example with Options
const redisClient = await createClient({
url: process.env.REDIS_URL || 'redis://localhost:6379',
// Optional: configure connection pooling, timeouts, etc.
}).connect();
app.use(ratewarden({
windowMs: 60000,
tiers: {
guest: 30,
free: 100,
pro: 1000
},
redisClient,
redisPrefix: 'myapp:ratelimit:', // Custom key prefix
onLimitReached: (req, res, info) => {
console.log(`Rate limit exceeded: ${info.tier}`);
}
}));When to Use Redis
Use Redis when:
- You have multiple app servers (horizontal scaling)
- You need consistent rate limits across servers
- You deploy with Docker/Kubernetes (multiple pods)
- You use serverless/edge functions (shared state needed)
Use in-memory when:
- Single server deployment
- Development/testing
- Simplicity is more important than distributed state
Redis Architecture
Under the hood, ratewarden uses Redis sorted sets for sliding window:
Key: ratewarden:{tier}:{identityKey}
Value: Sorted set of timestamps
Score: Request timestamp (ms)
TTL: 2x windowMs (auto-cleanup)Example:
ratewarden:free:user123 → ZSET {
1678900001234: "1678900001234-abc123",
1678900005678: "1678900005678-def456"
}Operations:
ZREMRANGEBYSCORE→ Remove expired requestsZCARD→ Count requests in windowZADD→ Add new request- Keys auto-expire via
EXPIREcommand
Benefits:
- Atomic operations (no race conditions)
- Distributed consistency
- Automatic cleanup via TTL
- O(log N) performance
HTTP Headers
ratewarden follows the IETF RateLimit Headers draft:
On Success (200)
RateLimit-Limit: 60
RateLimit-Remaining: 45
RateLimit-Reset: 1700000123On Limit Exceeded (429)
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 1700000123
Retry-After: 24
{
"error": "Too many requests",
"message": "Rate limit exceeded for tier 'free'",
"tier": "free",
"limit": 60,
"current": 60,
"retryAfter": 24
}Architecture
Algorithm: Sliding Window Counter
Instead of fixed buckets, ratewarden uses a sliding window:
Window = 60 seconds
Limit = 3 requests
Time: 0s 15s 30s 45s 60s
Req: ✓ ✓ ✓ ✗ ✓
OK OK OK 429 OK (first request expired)Benefits:
- More accurate than fixed windows
- No sudden bursts at window boundaries
- O(1) check per request
Memory Management
- Automatic cleanup of expired timestamps
- Background cleanup runs every minute
- No memory leaks on long-running servers
Comparison with Other Libraries
| Feature | ratewarden | express-rate-limit | rate-limiter-flexible | |---------|-----------|-------------------|---------------------| | Zero-config | Yes | No (requires config) | No (requires Redis) | | Identity-aware | Yes | No (IP only by default) | Manual setup | | Tier-based | Built-in | Custom code | Custom code | | Dependencies | None (Redis optional) | None | Redis required | | Distributed support | Yes (via Redis) | No | Yes | | Setup time | 30 seconds | 5 minutes | 30+ minutes | | Best for | Small to large APIs, MVPs to Production | Customizable setups | Enterprise only |
Configuration Options
interface RateGuardOptions {
// Time window in milliseconds (default: 60000)
windowMs?: number;
// Tier configuration (default: { free: 60, pro: 600, admin: Infinity, guest: 30 })
tiers?: Record<string, number>;
// Custom tier resolver
resolveTier?: (req) => string;
// Custom identity key generator
keyGenerator?: (req) => string;
// Callback when limit is reached
onLimitReached?: (req, res, info) => void;
// Redis client for distributed rate limiting (optional)
redisClient?: RedisClientType;
// Redis key prefix (default: 'ratewarden:')
redisPrefix?: string;
}Testing
Run tests:
npm testExpected output:
✓ All tests passed!
Passed: 15
Failed: 0Use Cases
Perfect for:
- Hackathon projects: Deploy protection in 30 seconds
- MVPs and prototypes: No infrastructure overhead
- SaaS APIs: Built-in tier support
- Learning projects: Simple, readable code
- Single-server deployments: Zero-config in-memory storage
- Multi-server deployments: Optional Redis for distributed state
Not ideal for:
- Extremely high traffic (>10k req/sec per endpoint per server)
- Complex sliding window with exact distributed fairness guarantees
- Sub-millisecond precision requirements
Roadmap
v1.0 (Released)
- Sliding window algorithm
- Identity resolution
- Tier-based limits
- Standard headers
- In-memory storage
v1.1 (Current)
- Redis adapter for distributed systems
- Store abstraction layer
- Backward-compatible API
v2.0 (Future)
- [ ] Prometheus metrics export
- [ ] Rate limit analytics dashboard
- [ ] Fastify and Koa adapters
- [ ] GraphQL query complexity integration
Contributing
Contributions welcome! Please:
- Fork the repo
- Create a feature branch
- Add tests for new features
- Submit a PR
License
MIT © Niraj Rajendra Naphade
Philosophy
Batteries included, but removable.
ratewarden makes the 90% use case trivial while still allowing customization for the other 10%.
Made for developers who want protection without complexity
