
Security News
arXiv Is Rate Limiting Authors Following a Flood of AI Slop Submissions
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.
flex-rate-limit
Advanced tools
Universal rate limiting module for Node.js - supports any framework, multiple storage backends, flexible algorithms
A framework-agnostic Node.js rate limiting library with multiple algorithms, Memory / Redis / cache-hub backed storage, and Express-style middleware.
check() from Express, Koa, Egg.js, Hapi, Fastify, workers, queues, or any custom adapter.limiter.middleware() directly in Express-compatible stacks.CacheHubStore, or your own store adapter.allowed, limit, current, remaining, resetTime, and retryAfter.>=18.0.0npm install flex-rate-limit
Redis-backed usage:
npm install flex-rate-limit ioredis
cache-hub atomic backend usage:
npm install flex-rate-limit cache-hub ioredis
check()const { RateLimiter } = require('flex-rate-limit');
const limiter = new RateLimiter({
windowMs: 15 * 60 * 1000,
max: 100,
});
const result = await limiter.check('user:123');
if (!result.allowed) {
console.log(`Retry after ${result.retryAfter} ms`);
}
const express = require('express');
const { RateLimiter } = require('flex-rate-limit');
const app = express();
const globalLimiter = new RateLimiter({
windowMs: 15 * 60 * 1000,
max: 100,
});
app.use(globalLimiter.middleware());
const loginLimiter = new RateLimiter({
windowMs: 15 * 60 * 1000,
max: 5,
});
app.post('/api/login', loginLimiter.middleware(), (_req, res) => {
res.json({ message: 'login accepted' });
});
app.listen(3000);
Use check() and map the result to the framework's own middleware, hook, or pre-handler shape:
const { RateLimiter } = require('flex-rate-limit');
const limiter = new RateLimiter({
windowMs: 60 * 1000,
max: 60,
});
async function guard(ctx, next) {
const key = `user:${ctx.user?.id || ctx.ip}:${ctx.path}`;
const result = await limiter.check(key, { route: ctx.path });
if (!result.allowed) {
ctx.status = 429;
ctx.body = { error: 'Too Many Requests', retryAfter: result.retryAfter };
return;
}
await next();
}
See the runnable framework examples on GitHub and the quickstart guide.
The default store is fast and simple. Use it for single-process services, local development, tests, and cases where counters do not need to be shared across instances.
const limiter = new RateLimiter({
windowMs: 60 * 1000,
max: 100,
store: 'memory',
});
Use RedisStore when multiple application instances must share counters.
const Redis = require('ioredis');
const { RateLimiter, RedisStore } = require('flex-rate-limit');
const redis = new Redis(process.env.REDIS_URL || 'redis://127.0.0.1:6379');
const limiter = new RateLimiter({
windowMs: 60 * 1000,
max: 100,
store: new RedisStore({ client: redis, prefix: 'rl:' }),
});
If you pass store: 'redis://...', RateLimiter creates and owns the Redis client; call await limiter.close() during shutdown. When you pass your own Redis client to RedisStore, that client remains caller-owned unless you set ownsClient: true.
CacheHubStore keeps flex-rate-limit as the algorithm and middleware layer while delegating high-concurrency state updates to cache-hub atomic primitives. Pass a Redis client for distributed production usage; omit the client only when an in-memory cache-hub backend is acceptable. The in-memory cache-hub path prunes expired rate-limit state automatically, and await limiter.close() closes store-owned cleanup timers or cache resources.
const Redis = require('ioredis');
const { RateLimiter, CacheHubStore } = require('flex-rate-limit');
const redis = new Redis(process.env.REDIS_URL || 'redis://127.0.0.1:6379');
const limiter = new RateLimiter({
algorithm: 'sliding-window',
windowMs: 60 * 1000,
max: 100,
store: new CacheHubStore({ client: redis, prefix: 'rl:' }),
});
Read more in the storage guide.
| Algorithm | Best For | Notes |
|---|---|---|
sliding-window | Precise rolling limits | Default algorithm; stores more per-key state |
fixed-window | High-throughput coarse windows | Fast, but requests can cluster around window boundaries |
token-bucket | Controlled bursts | Allows bursts up to capacity and refills over time |
leaky-bucket | Smoothing traffic | Drains at a steady rate |
Choose semantics first, then optimize storage and hot paths. See algorithm comparison and deep analysis.
const limiter = new RateLimiter({
windowMs: 60 * 1000,
max: 100,
algorithm: 'sliding-window',
headers: true,
keyGenerator: (req) => `user:${req.user?.id || req.ip}:${req.path}`,
skip: (req) => req.path === '/health',
handler: (req, res) => {
res.status(429).json({ error: 'Too Many Requests' });
},
perRoute: {
'/api/login': { max: 5, windowMs: 15 * 60 * 1000 },
'/api/users': { max: 100, windowMs: 60 * 1000 },
},
});
| Option | Default | Description |
|---|---|---|
windowMs | 60000 | Time window in milliseconds |
max | 100 | Max requests per window; may be a function |
algorithm | sliding-window | One of the four supported algorithms |
store | memory | Store instance or 'memory' |
keyGenerator | IP-based | Builds the rate-limit key from request context |
skip | () => false | Return true to bypass rate limiting |
handler | built-in 429 response | Custom over-limit handler |
headers | true | Write X-RateLimit-* and Retry-After headers |
perRoute | null | Route-specific overrides |
skipSuccessfulRequests | false | Roll back successful responses |
skipFailedRequests | false | Roll back failed responses |
Full details are in the configuration guide and API reference.
{
allowed: true,
limit: 100,
current: 1,
remaining: 99,
resetTime: 1767225600000,
retryAfter: 0
}
The repository includes reproducible local benchmark scripts for Memory direct checks, Redis direct checks, and HTTP middleware scenarios.
Run these from a cloned repository after installing development dependencies:
npm install
npm run benchmark:memory
npm run benchmark:redis
npm run benchmark:http
The npm runtime package does not require benchmark dependencies. Redis and HTTP benchmark output records the Node.js version, Redis URL, key distribution, concurrency, and other parameters. Use BENCH_JSON=1 when you need machine-readable output.
See Benchmark and Performance for commands, environment variables, and interpretation notes.
| Entry | Description |
|---|---|
| Documentation index | English documentation navigation |
| Chinese documentation | Simplified Chinese documentation navigation |
| Quickstart | First integration path and framework examples |
| Configuration | Complete option reference and practical presets |
| Storage | Memory, Redis, and CacheHubStore selection |
| Business lock guide | User + route scoped rate limiting |
| Algorithm comparison | Choosing the right algorithm |
| API reference | Classes, options, stores, and exports |
| Benchmark guide | Local benchmark commands and caveats |
The website is built with Rspress and reuses the docs/ directory:
npm run docs:dev
npm run docs:validate
npm run docs:build
Runnable examples are available on GitHub:
| Category | Files |
|---|---|
| Quickstart | quickstart-express.js, quickstart-koa.js, quickstart-egg.js, quickstart-hapi.js, quickstart-fastify.js |
| Router examples | express-router-example.js, koa-router-example.js, egg-router-example.js, fastify-router-example.js |
| IP whitelist examples | express-ip-whitelist-independent.js, koa-ip-whitelist-independent.js, ip-whitelist-example.js |
| Standalone usage | standalone-example.js |
| Business lock | egg-business-lock-example.js |
npm test
npm run test:unit
npm run test:integration
npm run typecheck
npm run lint
npm run coverage
RedisStore or CacheHubStore with a Redis client instead of the default Memory store.REDIS_URL / BENCH_REDIS_URL.ioredis.await limiter.close() when using store: 'redis://...', or pass ownsClient: true to RedisStore if the store should close your client.check() directly and wrap the result in the framework's native middleware or hook style.FAQs
Universal rate limiting module for Node.js - supports any framework, multiple storage backends, flexible algorithms
The npm package flex-rate-limit receives a total of 33 weekly downloads. As such, flex-rate-limit popularity was classified as not popular.
We found that flex-rate-limit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.

Research
/Security News
A new GhostAction wave hits hundreds of GitHub repos, expanding CI/CD secret theft to cloud and AI credentials in source code and git history.

Research
/Security News
Tensorlake npm SDK version 0.5.144 was compromised in a ChainDrop / Shai-Hulud attack, delivering credential-stealing malware.