Production-grade rate limiting, abuse detection, and Redis-backed counters as Express middleware.
| Capability | Description |
|---|---|
| Sliding / Fixed Window | Choose per-policy rate-limit algorithms |
| IP / User / IP+User Scopes | Apply limits independently to different principals |
| Redis-backed counters | Atomic increments, TTL-managed windows, horizontally scalable |
| Fail-open / Fail-closed | Configurable enforcement when Redis is unavailable |
| Metrics & Logging | Pino-compatible structured logging |
| Configurable Policies | JSON-based declarative policy definitions |
┌──────────────────────┐
│ Client (IP) │
└──────────┬───────────┘
│
▼
┌────────────────────────┐
│ Express / API Layer │
└────────────┬───────────┘
│
┌───────────────▼───────────────┐
│ Gatekeeper Middleware │
│ • extract IP / User │
│ • iterate policies │
│ • check counters (Redis) │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ RedisRateLimiter │
│ • fixedWindow: INCR + TTL │
│ • slidingWindow: ZSET │
└───────────────┬───────────────┘
│
┌─────▼─────┐
│ Redis │
└───────────┘
- Fixed Window – Simple INCR counter per window; cheap but allows minor burst at window boundaries.
- Sliding Window – Uses a Sorted Set (ZSET) to record timestamps. Removes entries older than window start, then counts.
| Mode | Behavior when Redis is down |
|---|---|
| fail-open (default) | Request is allowed through; logs error |
| fail-closed | Immediately responds with 503 |
npm install # or yarn install
npm run build # compiles TypeScript
npm test # runs Jest test suiteimport express from 'express';
import { Gatekeeper } from 'gatekeeper';
const app = express();
const gatekeeper = new Gatekeeper({
policies: [
{
id: 'ip-basic',
algorithm: 'sliding-window',
limit: 100,
windowSeconds: 60,
scope: 'ip',
},
{
id: 'user-tight',
algorithm: 'fixed-window',
limit: 20,
windowSeconds: 60,
scope: 'user',
},
],
identifyUser: (req) => req.headers['x-user-id'] as string | undefined,
});
app.use(gatekeeper.middleware());
app.get('/data', (_req, res) => res.json({ data: 'protected' }));
app.listen(3000);| Property | Type | Description |
|---|---|---|
policies |
RateLimitPolicy[] |
Array of rate-limit policies |
redisUrl |
string |
Redis connection string (default redis://localhost:6379) |
redisClient |
ioredis |
(optional) Bring-your-own Redis client |
redisPrefix |
string |
Key prefix (default gatekeeper) |
mode |
'fail-open' | 'fail-closed' |
Behavior when Redis fails |
logger |
GatekeeperLogger |
Pino-compatible logger |
identifyUser |
(req: Request) => string | undefined |
Extract user ID from request |
| Property | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
algorithm |
'fixed-window' | 'sliding-window' |
Algorithm |
limit |
number |
Max requests in window |
windowSeconds |
number |
Window length |
scope |
'ip' | 'user' | 'ip-user' |
Key scope |
keyPrefix |
string |
(optional) Custom Redis key prefix |
penaltySeconds |
number |
(reserved) |
description |
string |
Human-readable description |
docker-compose up --buildThe example app will be available at http://localhost:3000.
npm testTests use ioredis-mock to avoid needing a real Redis instance.
Gatekeeper/
├── src/
│ ├── counter.ts # Fixed & Sliding window logic
│ ├── gatekeeper.ts # Middleware class
│ ├── redis.ts # Redis client factory
│ ├── types.ts # TypeScript interfaces
│ └── index.ts # Public exports
├── examples/
│ └── express-app.ts # Integration demo
├── tests/
│ └── gatekeeper.test.ts
├── Dockerfile
├── docker-compose.yml
├── package.json
├── tsconfig.json
└── README.md
- IP spoofing: Trust
X-Forwarded-Foronly behind a trusted proxy. - Redis hardening: Use ACLs and TLS in production.
- Key entropy: Salting keys can prevent attackers from predicting patterns.
MIT