web-gatekeeper-js
v1.2.6
Published
Redis based rate limiter and throttler using sliding window and token bucket algorithms
Downloads
2,676
Maintainers
Readme
web-gatekeeper-js
A high-performance, Redis-powered rate limiter and throttler for Node.js applications.
Built for distributed systems, horizontally scalable architectures, and high concurrency environments.
Supports:
- Sliding Window Counter rate limiting
- Token Bucket throttling
- Atomic operations using Lua scripts
- Distributed environments using Redis
- Zero race conditions
- High concurrency handling
- Express, Fastify, NestJS, Koa, and custom Node.js servers
Features
- ⚡ Extremely fast Redis-based implementation
- 🔒 Atomic operations using Redis Lua scripts
- 🌐 Works across multiple servers/instances
- 📈 Horizontally scalable
- 🧠 Supports burst traffic handling
- 🚫 Prevents race conditions in concurrent environments
- 🪶 Lightweight and dependency minimal
- 🔧 Fully configurable
- 🧵 Safe under heavy parallel requests
- ♻️ Reusable Redis connections
- 📦 TypeScript support
Why Redis?
Traditional in-memory rate limiters work only on a single server instance.
That becomes a problem when your application is deployed across:
- Multiple containers
- Multiple Node.js processes
- Kubernetes clusters
- Load balanced servers
- Microservices
Redis acts as a centralized shared datastore, allowing every instance of your application to enforce the same limits consistently.
This package uses Redis for:
- Shared state management
- Atomic counters
- Distributed synchronization
- Fast in-memory performance
Why Lua Scripts?
Redis commands executed separately can lead to race conditions.
Example problem:
Two requests arrive simultaneously.
Both requests:
- Read current token count
- Both think tokens are available
- Both consume tokens
- Limit gets bypassed
This package avoids that entirely using Redis Lua scripts.
Lua scripts run atomically inside Redis, meaning:
- No other Redis command can interrupt execution
- Read + update operations happen together
- Concurrent requests remain safe
- No locks are required
This guarantees correctness even under massive traffic spikes.
Algorithms Used
Sliding Window Counter (Rate Limiter)
Used by RateLimiter.
Tracks requests within a moving time window.
Example:
- Limit:
100 requests - Window:
60 seconds
The limiter continuously tracks requests within the last 60 seconds instead of resetting abruptly like fixed window algorithms.
Benefits
- Smoother limiting
- More accurate than fixed windows
- Prevents sudden request bursts at window boundaries
- Better user experience
Token Bucket (Throttler)
Used by Throttler.
Tokens refill gradually over time.
Example:
- Bucket size:
10 - Refill rate:
2 tokens/sec
If the bucket has tokens available, requests are allowed immediately.
If not:
- Requests are delayed/rejected
- Burst traffic is controlled gracefully
Benefits
- Handles traffic spikes efficiently
- Allows short bursts
- Smooth request flow
- Ideal for APIs and real-time systems
Installation
npm install gatekeeper-js ioredisQuick Start
Rate Limiter Example
import { RateLimiter } from 'gatekeeper-js'
import Redis from 'ioredis'
const redis = new Redis()
const limiter = new RateLimiter({
redisClient : redis,
windowSize : 60000,
limit : 100,
maxToken : 10,
refillRate : 2
})
app.use(async (req, res, next) => {
const result = await limiter.consume(req.ip)
if (!result.allowed) {
return res.status(429).json({
error: result.reason
})
}
next()
})Throttler Example
import { Throttler } from 'gatekeeper-js'
import Redis from 'ioredis'
const redis = new Redis()
const throttler = new Throttler({
redisClient : redis,
refillRate : 2,
maxWait : 5000
})
app.use(async (req, res, next) => {
const result = await throttler.consume(req.ip)
if (!result.allowed) {
return res.status(429).json({
error: 'Too Many Requests'
})
}
next()
})API
RateLimiter
Constructor
new RateLimiter(options)Options
| Option | Type | Required | Description | |---|---|---|---| | redisClient | Redis | Yes | ioredis client instance | | windowSize | number | Yes | Time window in milliseconds | | limit | number | Yes | Maximum requests allowed per window | | maxToken | number | No | Maximum burst capacity | | refillRate | number | No | Token refill rate per second |
consume(key)
Consumes one request for a given identifier.
const result = await limiter.consume('user-id')Response
{
allowed: true,
remaining: 42,
resetTime: 1716200000000
}Response Fields
| Field | Description | |---|---| | allowed | Whether request is allowed | | remaining | Remaining requests/tokens | | resetTime | Unix timestamp when limit resets | | reason | Rejection reason if blocked |
Throttler
Constructor
new Throttler(options)Options
| Option | Type | Required | Description | |---|---|---|---| | redisClient | Redis | Yes | ioredis client instance | | refillRate | number | Yes | Tokens added per second | | maxWait | number | No | Maximum wait time before rejection |
consume(key)
const result = await throttler.consume('user-id')Response
{
allowed: true,
retryAfter: 0
}Scalability
This package is designed for distributed systems.
Because Redis stores the state centrally:
- Multiple Node.js instances share the same limits
- Limits remain consistent across deployments
- Works seamlessly behind load balancers
- Suitable for microservices and Kubernetes
Example architecture:
Client Requests
↓
Load Balancer
↓
┌─────────────┐
│ Node App 1 │
├─────────────┤
│ Node App 2 │
├─────────────┤
│ Node App 3 │
└─────────────┘
↓
RedisAll application instances communicate with the same Redis server.
Concurrency Safety
This package is safe under heavy concurrent traffic.
Redis Lua scripts ensure:
- Atomic execution
- No partial updates
- No inconsistent counters
- No race conditions
Even if thousands of requests arrive simultaneously, limits remain accurate.
Performance
Redis operations are extremely fast because Redis stores data in memory.
The package is optimized to:
- Minimize Redis calls
- Use atomic Lua execution
- Reduce network overhead
- Handle high throughput efficiently
Suitable for:
- Public APIs
- Authentication systems
- Login protection
- Payment APIs
- WebSocket gateways
- Real-time applications
Redis Compatibility
Compatible with:
- Redis 6+
- Redis 7+
- Redis Cluster
- Redis Cloud providers
Framework Support
Works with any Node.js framework:
- Express
- Fastify
- NestJS
- Koa
- Hono
- Native HTTP servers
Error Handling
Example:
try {
const result = await limiter.consume(req.ip)
if (!result.allowed) {
return res.status(429).json({
error: result.reason
})
}
next()
} catch (error) {
console.error(error)
return res.status(500).json({
error: 'Internal Server Error'
})
}Best Practices
Use Stable Keys
Good examples:
req.ip
user.id
apiKeyAvoid random or frequently changing keys.
Reuse Redis Connections
Create one shared Redis client instance and reuse it across the application.
const redis = new Redis()Configure Reasonable Limits
| Use Case | Suggested Limit | |---|---| | Login API | 5 requests/min | | Public API | 100 requests/min | | OTP Verification | 3 requests/5 min |
License
ISC License
Permission to use, modify, and distribute this software for any purpose with or without fee is hereby granted.
See the LICENSE file for full details.
Contributing
Contributions are welcome.
Feel free to:
- Open issues
- Suggest improvements
- Submit pull requests
Roadmap
Planned features:
- Redis Cluster optimizations
- Sliding log algorithm
- Fixed window limiter
- Rate limit headers
- Automatic retries
- Memory fallback store
- Metrics integration
Author
Built for modern distributed Node.js applications using Redis and Lua for correctness, scalability, and performance.
