redis-traffic-limiter 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +115 -0
- package/example/FixedWindow.js +27 -0
- package/example/SlidingWindow.js +25 -0
- package/package.json +28 -0
- package/src/index.js +2 -0
- package/src/limiter/FixedWindow.js +32 -0
- package/src/limiter/SlidingWindow.js +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Redis Rate Limiter
|
|
2
|
+
|
|
3
|
+
A lightweight Redis-based rate limiter for Node.js using Redis.
|
|
4
|
+
|
|
5
|
+
Currently supported algorithms:
|
|
6
|
+
|
|
7
|
+
- ✅ Fixed Window
|
|
8
|
+
- ✅ Sliding Window
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
Before using this package, make sure you have:
|
|
15
|
+
|
|
16
|
+
- Node.js
|
|
17
|
+
- Redis Server
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Install Redis
|
|
22
|
+
|
|
23
|
+
### Ubuntu
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
sudo apt update
|
|
27
|
+
sudo apt install redis-server
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Start Redis
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
redis-server
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Windows
|
|
37
|
+
|
|
38
|
+
Run Redis using Docker:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
docker run -d \
|
|
42
|
+
--name redis \
|
|
43
|
+
-p 6379:6379 \
|
|
44
|
+
redis
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
Install the package along with the official Redis client.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install redis-traffic-limiter redis
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Create a Redis Client
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { createClient } from "redis";
|
|
63
|
+
|
|
64
|
+
const redis = createClient({
|
|
65
|
+
url: "redis://localhost:6379"
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await redis.connect();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Pass the connected Redis client while creating the limiter.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
Example programs are available in the `examples/` directory.
|
|
78
|
+
|
|
79
|
+
- `examples/fixed.js`
|
|
80
|
+
- `examples/sliding.js`
|
|
81
|
+
|
|
82
|
+
Run them using:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node examples/fixed.js
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
or
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
node examples/sliding.js
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Supported Algorithms
|
|
97
|
+
|
|
98
|
+
- Fixed Window
|
|
99
|
+
- Sliding Window
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Notes
|
|
104
|
+
|
|
105
|
+
- The package expects a **connected node-redis client**.
|
|
106
|
+
- Redis connections are managed by your application.
|
|
107
|
+
- `window` is specified in **milliseconds**.
|
|
108
|
+
- Currently supports the official **node-redis** client only.
|
|
109
|
+
- Support for additional Redis clients (e.g. ioredis) is planned for future releases.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createClient } from "redis";
|
|
2
|
+
import { FixedWindow } from "../src/index.js";
|
|
3
|
+
|
|
4
|
+
// Create a Redis client.
|
|
5
|
+
// By default Redis runs on localhost:6379.
|
|
6
|
+
const redis = createClient({
|
|
7
|
+
url: "redis://localhost:6379"
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// Connect to Redis before using it.
|
|
11
|
+
await redis.connect();
|
|
12
|
+
|
|
13
|
+
// Create a Fixed Window limiter.
|
|
14
|
+
// window is in milliseconds.
|
|
15
|
+
const limiter = new FixedWindow({
|
|
16
|
+
redis,
|
|
17
|
+
window: 60000,
|
|
18
|
+
max: 5
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Consume one request for this user.
|
|
22
|
+
const result = await limiter.consume("user:1");
|
|
23
|
+
|
|
24
|
+
console.log(result);
|
|
25
|
+
|
|
26
|
+
// Close the Redis connection.
|
|
27
|
+
await redis.quit();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createClient } from "redis";
|
|
2
|
+
import { SlidingWindow } from "../src/index.js";
|
|
3
|
+
|
|
4
|
+
// Create a Redis client.
|
|
5
|
+
const redis = createClient({
|
|
6
|
+
url: "redis://localhost:6379"
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// Connect to Redis.
|
|
10
|
+
await redis.connect();
|
|
11
|
+
|
|
12
|
+
// Create a Sliding Window limiter.
|
|
13
|
+
const limiter = new SlidingWindow({
|
|
14
|
+
redis,
|
|
15
|
+
window: 60000,
|
|
16
|
+
max: 5
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Consume one request.
|
|
20
|
+
const result = await limiter.consume("user:1");
|
|
21
|
+
|
|
22
|
+
console.log(result);
|
|
23
|
+
|
|
24
|
+
// Close Redis connection.
|
|
25
|
+
await redis.quit();
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "redis-traffic-limiter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight Redis-based rate limiter for Node.js supporting Fixed Window and Sliding Window algorithms.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"redis",
|
|
12
|
+
"rate-limiter",
|
|
13
|
+
"traffic",
|
|
14
|
+
"limiter",
|
|
15
|
+
"nodejs",
|
|
16
|
+
"backend",
|
|
17
|
+
"fixed-window",
|
|
18
|
+
"sliding-window"
|
|
19
|
+
],
|
|
20
|
+
"author": "Bhuvan",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"redis": "^6.1.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class FixedWindow {
|
|
2
|
+
|
|
3
|
+
constructor({ redis, window, max }) {
|
|
4
|
+
this.redis = redis;
|
|
5
|
+
this.window = window;
|
|
6
|
+
this.max = max;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async consume(key) {
|
|
10
|
+
|
|
11
|
+
const count = await this.redis.incr(key);
|
|
12
|
+
|
|
13
|
+
if (count === 1) {
|
|
14
|
+
await this.redis.expire(key, this.window);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (count <= this.max) {
|
|
18
|
+
return {
|
|
19
|
+
allowed: true,
|
|
20
|
+
remaining: this.max - count
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
allowed: false,
|
|
26
|
+
remaining: 0
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default FixedWindow;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class SlidingWindow {
|
|
2
|
+
|
|
3
|
+
constructor({ redis, window, max }) {
|
|
4
|
+
this.redis = redis;
|
|
5
|
+
this.window = window;
|
|
6
|
+
this.max = max;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async consume(key) {
|
|
10
|
+
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
await this.redis.zRemRangeByScore(
|
|
15
|
+
key,
|
|
16
|
+
0,
|
|
17
|
+
now - this.window
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const count = await this.redis.zCard(key);
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if (count >= this.max) {
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
allowed: false,
|
|
28
|
+
remaining: 0
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
await this.redis.zAdd(key, {
|
|
35
|
+
score: now,
|
|
36
|
+
value: `${now}-${Math.random()}`
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
await this.redis.expire(
|
|
41
|
+
key,
|
|
42
|
+
Math.ceil(this.window / 1000)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
allowed: true,
|
|
47
|
+
remaining: this.max - count - 1
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default SlidingWindow;
|