Installations
npm install rolling-rate-limiter
Developer Guide
Typescript
Yes
Module System
CommonJS
Min. Node Version
>= 12.0.0
Score
96.6
Supply Chain
99
Quality
76.2
Maintenance
100
Vulnerability
100
License
Releases
Unable to fetch releases
Contributors
Unable to fetch Contributors
Languages
TypeScript (98.86%)
JavaScript (1.14%)
Developer
peterkhayes
Download Statistics
Total Downloads
4,888,360
Last Day
7,287
Last Week
40,252
Last Month
192,914
Last Year
1,282,567
GitHub Statistics
348 Stars
107 Commits
53 Forks
29 Watching
2 Branches
16 Contributors
Bundle Size
15.83 kB
Minified
5.95 kB
Minified + Gzipped
Package Meta Information
Latest Version
0.4.2
Package Id
rolling-rate-limiter@0.4.2
Unpacked Size
170.65 kB
Size
47.19 kB
File Count
14
Publised On
14 Feb 2023
Total Downloads
Cumulative downloads
Total Downloads
4,888,360
Last day
-2.3%
7,287
Compared to previous day
Last week
-12.2%
40,252
Compared to previous week
Last month
14.1%
192,914
Compared to previous month
Last year
15.5%
1,282,567
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Rolling Rate Limiter
This is an implementation of a rate limiter in node.js that allows for rate limiting with a rolling window. It can use either in-memory storage or Redis as a backend. If Redis is used, multiple rate limiters can share one instance with different namespaces, and multiple processes can share rate limiter state safely.
This means that if a user is allowed 5 actions per 60 seconds, any action will be blocked if 5 actions have already occured in the preceeding 60 seconds, without any set points at which this interval resets. This contrasts with some other rate limiter implementations, in which a user could make 5 requests at 0:59 and another 5 requests at 1:01.
Important Note: As a consequence of the way the Redis algorithm works, if an action is blocked, it is still "counted". This means that if a user is continually attempting actions more quickly than the allowed rate, all of their actions will be blocked until they pause or slow their requests.
This behavior is somewhat counterintuitive, but it's the only way that I have found that uses an atomic MULTI
set of commands for Redis. Without this, race conditions would be possible. See more below..
Quick start
Basic use in an Express application.
1const { RedisRateLimiter } = require("rolling-rate-limiter"); 2 3const limiter = new RedisRateLimiter({ 4 client: redisClient, // client instance from `redis` or `ioredis` 5 namespace: "rate-limiter", // prefix for redis keys 6 interval: 60000, // milliseconds 7 maxInInterval: 5, 8}); 9 10app.use(function (req, res, next) { 11 limiter.limit(req.ipAddress).then((wasBlocked) => { 12 if (wasBlocked) { 13 return res.status(429).send("Too many requests"); 14 } else { 15 return next(); 16 } 17 }); 18});
Available limiters
InMemoryRateLimiter
- Stores state in memory. Useful in testing or outside of web servers.- Redis rate limiters: There are two main redis clients for node: redis (aka node-redis) and ioredis. Both are supported:
RedisRateLimiter
- Attempts to detect whether it was passed aredis
orioredis
client.NodeRedisRateLimiter
- No detection; only works withredis
client.IORedisRateLimiter
- No detection; only works withioredis
client.
Configuration options
interval: number
- The length of the rate limiter's interval, in milliseconds. For example, if you want a user to be able to perform 5 actions per minute, this should be60000
.maxInInterval: number
- The number of actions allowed in each interval. For example, in the scenario above, this would be5
minDifference?: number
- Optional. The minimum time allowed between consecutive actions, in milliseconds.client: Client
(Redis only) - The Redis client to use.namespace: string
(Redis only) - A string to prepend to all keys to prevent conflicts with other code using Redis.
Instance Methods
All methods take an Id
, which should be of type number | string
. Commonly, this will be a user's id.
limit(id: Id): Promise<boolean>
- Attempt to perform an action. Returnsfalse
if the action should be allowed, andtrue
if the action should be blocked.wouldLimit(id: Id): Promise<boolean>
- Return what would happen if an action were attempted. Returnsfalse
if an action would not have been blocked, andtrue
if an action would have been blocked. Does not "count" as an action.limitWithInfo(id: Id): Promise<RateLimitInfo>
- Attempt to perform an action. Returns whether the action should be blocked, as well as additional information about why it was blocked and how long the user must wait.wouldLimitWithInfo(id: Id): Promise<RateLimitInfo>
- Returns info about what would happened if an action were attempted and why. Does not "count" as an action.
RateLimitInfo
contains the following properties:
blocked: boolean
- Whether the action was blocked (or would have been blocked).blockedDueToCount: boolean
- Whether the action was blocked (or would have been blocked) because of theinterval
andmaxInInterval
properties.blockedDueToMinDifference: boolean
- Whether the action was blocked (or would have been blocked) because of theminDistance
property.millisecondsUntilAllowed: number
- The number of milliseconds the user must wait until they can make another action. If another action would immediately be permitted, this is0
.actionsRemaining: number
- The number of actions a user has left within the interval. Does not account forminDifference
.
Method of operation
- Each identifier/user corresponds to a sorted set data structure. The keys and values are both equal to the (microsecond) times at which actions were attempted, allowing easy manipulation of this list.
- When a new action comes in for a user, all elements in the set that occurred earlier than (current time - interval) are dropped from the set.
- If the number of elements in the set is still greater than the maximum, the current action is blocked.
- If a minimum difference has been set and the most recent previous element is too close to the current time, the current action is blocked.
- The current action is then added to the set.
- Note: if an action is blocked, it is still added to the set. This means that if a user is continually attempting actions more quickly than the allowed rate, all of their actions will be blocked until they pause or slow their requests.
- If the limiter uses a redis instance, the keys are prefixed with namespace, allowing a single redis instance to support separate rate limiters.
- All redis operations for a single rate-limit check/update are performed as an atomic transaction, allowing rate limiters running on separate processes or machines to share state safely.
Local development
Installation
Install dependencies with yarn
.
To run tests, you will need to have a Redis server running. You can do this by installing Redis, and running redis-server
. Alternatively, you can run the CI build, which includes tests, by installing act. This requires Docker to be running - on MacOS that means running Docker.app
from your Applications
folder.
Testing
yarn ci
: Runs the CI build, including linting, type checking, and tests. Requires act to run GitHub actions locally.yarn lint
: Runs ESLint.yarn test
: Runs Jest.yarn typecheck
: Runs TypeScript, without emitting output.yarn build
: Runs TypeScript and outputs to./lib
.
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
Found 3/20 approved changesets -- score normalized to 1
Reason
9 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9
- Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/ci.yml:1
- Info: no jobLevel write permissions found
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/peterkhayes/rolling-rate-limiter/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/peterkhayes/rolling-rate-limiter/ci.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/peterkhayes/rolling-rate-limiter/ci.yml/master?enable=pin
- Info: 0 out of 2 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 1 third-party GitHubAction dependencies pinned
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 13 are checked with a SAST tool
Score
2.7
/10
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to rolling-rate-limiter
@types/rolling-rate-limiter
Stub TypeScript definitions entry for rolling-rate-limiter, which provides its own types definitions
rolling-rate-limiter-forked
Rate limiter that supports a rolling window, either in-memory or backed by redis
@fuechschen/rolling-rate-limiter
Rate limiter that supports a rolling window, either in-memory or backed by redis
redis-rolling-rate-limiter
Rolling Rate Limiter is a module for Node.js which provides a basic, but a solid rate limiter using sliding windows stored in Redis.