@clov-std/elysia-ratelimit 4.0.17 → 4.0.18
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 +25 -58
- package/package.json +7 -11
package/README.md
CHANGED
|
@@ -2,53 +2,25 @@
|
|
|
2
2
|
<img src="https://cdn.jsdelivr.net/gh/ClovLabs/std@main/packages/elysia-ratelimit/logo-elysia-ratelimit.png" alt="Clov Elysia Rate Limit logo" width="200" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# Clov Elysia Rate Limit
|
|
6
6
|
|
|
7
|
-
Rate limiting for Elysia
|
|
8
|
-
Drop `rateLimit` on any endpoint and abusive traffic gets cut before it ever hits your logic.
|
|
7
|
+
Rate limiting for Elysia, as a macro. Put `rateLimit: { limit: 10, window: 60 }` on a route, guard or group and you're done.
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
It runs in `transform`, the first per-route hook, so excess requests are rejected before auth guards or handlers do any work. Counters are per client IP by default, which suits login endpoints. When lots of users share one IP (an office, a corporate proxy), a `keyGenerator` lets you count per token, session, API key, or whatever fits.
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
You add `rateLimit: { limit: 10, window: 60 }` and you're done.
|
|
14
|
-
|
|
15
|
-
By default it limits by client IP, which works great for auth endpoints.
|
|
16
|
-
For cases where many users share the same public IP (offices, corporate proxies), you can pass a `keyGenerator` to rate limit by IP + access token, session ID, API key, or any combination that makes sense.
|
|
17
|
-
|
|
18
|
-
Storage is handled by `@clov-std/kv-store`, so you start with in-memory and move to Redis when you need to, without changing your routes.
|
|
19
|
-
|
|
20
|
-
## 📌 Table of Contents
|
|
21
|
-
|
|
22
|
-
- [Features](#-features)
|
|
23
|
-
- [Installation](#-installation)
|
|
24
|
-
- [Usage](#-usage)
|
|
25
|
-
- [Error handling](#-error-handling)
|
|
26
|
-
- [API Reference](#-api-reference)
|
|
27
|
-
- [License](#-license)
|
|
28
|
-
- [Contact](#-contact)
|
|
29
|
-
|
|
30
|
-
## ✨ Features
|
|
31
|
-
|
|
32
|
-
- 🎯 **Per-route macros** : Attach `rateLimit` to any route independently, with its own `limit` and `window`.
|
|
33
|
-
- 🔑 **Custom key generation** : Rate limit by IP (default), IP + token, session, or any key you compute.
|
|
34
|
-
- 🗃️ **KvStore-agnostic** : Works with `MemoryStore` out of the box; swap in `BunRedisStore` or your own adapter.
|
|
35
|
-
- ⚡ **Early rejection** : Runs in `transform`, the first per-route hook, before auth guards and handlers.
|
|
36
|
-
- 📡 **Standard headers** : Automatically sets `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`.
|
|
37
|
-
- 🧯 **Complete RFC 9457 `429`** : Answers a real problem document on its own, `detail` included, with no exception class to import.
|
|
38
|
-
|
|
39
|
-
## 🔧 Installation
|
|
11
|
+
Storage goes through `@clov-std/kv-store`: memory by default, Redis when you need it, same routes either way.
|
|
40
12
|
|
|
41
13
|
```bash
|
|
42
14
|
bun add @clov-std/elysia-ratelimit elysia typebox
|
|
43
15
|
```
|
|
44
16
|
|
|
45
|
-
|
|
17
|
+
`elysia` (v2) and `typebox` are peer dependencies.
|
|
46
18
|
|
|
47
|
-
##
|
|
19
|
+
## Usage
|
|
48
20
|
|
|
49
|
-
### Basic
|
|
21
|
+
### Basic
|
|
50
22
|
|
|
51
|
-
|
|
23
|
+
`limit` is the max number of requests, `window` is in seconds.
|
|
52
24
|
|
|
53
25
|
```ts
|
|
54
26
|
import { rateLimitPlugin } from '@clov-std/elysia-ratelimit';
|
|
@@ -64,9 +36,9 @@ new Elysia()
|
|
|
64
36
|
.listen(3000);
|
|
65
37
|
```
|
|
66
38
|
|
|
67
|
-
###
|
|
39
|
+
### Redis
|
|
68
40
|
|
|
69
|
-
|
|
41
|
+
Pass a `BunRedisStore` (or any `KvStore` adapter) to keep counters across restarts and share them between instances.
|
|
70
42
|
|
|
71
43
|
```ts
|
|
72
44
|
import { rateLimitPlugin } from '@clov-std/elysia-ratelimit';
|
|
@@ -81,10 +53,9 @@ new Elysia()
|
|
|
81
53
|
.listen(3000);
|
|
82
54
|
```
|
|
83
55
|
|
|
84
|
-
###
|
|
56
|
+
### Per user instead of per IP
|
|
85
57
|
|
|
86
|
-
|
|
87
|
-
Each user has their own counter, independent of their network.
|
|
58
|
+
For authenticated routes behind a shared IP, give each user their own counter:
|
|
88
59
|
|
|
89
60
|
```ts
|
|
90
61
|
import { rateLimitPlugin } from '@clov-std/elysia-ratelimit';
|
|
@@ -107,12 +78,13 @@ new Elysia()
|
|
|
107
78
|
.listen(3000);
|
|
108
79
|
```
|
|
109
80
|
|
|
110
|
-
|
|
81
|
+
`extractClientIp` is exported if you need it in your own `keyGenerator`.
|
|
82
|
+
|
|
83
|
+
## Headers and the 429
|
|
111
84
|
|
|
112
|
-
|
|
85
|
+
Every response gets `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.
|
|
113
86
|
|
|
114
|
-
|
|
115
|
-
RFC 9457 document with no exception class and no error dependency involved:
|
|
87
|
+
Once the limit is hit, the plugin throws Elysia's `problem()`, so the `429` is a full RFC 9457 document (`application/problem+json`) without any exception class to import:
|
|
116
88
|
|
|
117
89
|
```json
|
|
118
90
|
{
|
|
@@ -123,21 +95,16 @@ RFC 9457 document with no exception class and no error dependency involved:
|
|
|
123
95
|
}
|
|
124
96
|
```
|
|
125
97
|
|
|
126
|
-
`type` is always `RATE_LIMIT_ERROR_CODES.QUOTA_EXCEEDED`.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
the throw so they survive on the `429`.
|
|
130
|
-
|
|
131
|
-
This is also the response the macro declares for OpenAPI, so a typed client sees it.
|
|
132
|
-
|
|
133
|
-
## 📚 API Reference
|
|
98
|
+
- `type` is always `RATE_LIMIT_ERROR_CODES.QUOTA_EXCEEDED`.
|
|
99
|
+
- `title` comes from Elysia, based on the status.
|
|
100
|
+
- `detail` reflects the current counter, so it changes from one 429 to the next.
|
|
134
101
|
|
|
135
|
-
|
|
102
|
+
The headers are set before the throw, so they're still on the `429`. The macro also declares this response for OpenAPI, so typed clients know about it.
|
|
136
103
|
|
|
137
|
-
##
|
|
104
|
+
## Docs
|
|
138
105
|
|
|
139
|
-
|
|
106
|
+
API reference: [clovlabs.github.io/std](https://clovlabs.github.io/std/)
|
|
140
107
|
|
|
141
|
-
##
|
|
108
|
+
## License
|
|
142
109
|
|
|
143
|
-
|
|
110
|
+
MIT, see [LICENSE.md](LICENSE.md).
|
package/package.json
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clov-std/elysia-ratelimit",
|
|
3
|
-
"version": "4.0.
|
|
4
|
-
"description": "Rate limiting
|
|
3
|
+
"version": "4.0.18",
|
|
4
|
+
"description": "Rate limiting for Elysia as a route macro, with RFC 9457 429 responses and pluggable storage.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"bun",
|
|
7
|
-
"clov",
|
|
8
6
|
"elysia",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"ratelimit",
|
|
13
|
-
"typescript"
|
|
7
|
+
"elysia-plugin",
|
|
8
|
+
"rate-limit",
|
|
9
|
+
"ratelimit"
|
|
14
10
|
],
|
|
15
11
|
"license": "MIT",
|
|
16
12
|
"author": "Clov",
|
|
@@ -40,7 +36,7 @@
|
|
|
40
36
|
"test": "bun test --pass-with-no-tests --coverage"
|
|
41
37
|
},
|
|
42
38
|
"dependencies": {
|
|
43
|
-
"@clov-std/kv-store": "^2.0.
|
|
39
|
+
"@clov-std/kv-store": "^2.0.4"
|
|
44
40
|
},
|
|
45
41
|
"devDependencies": {
|
|
46
42
|
"@types/bun": "^1.4.2",
|
|
@@ -50,7 +46,7 @@
|
|
|
50
46
|
"typescript": "^7.0.2"
|
|
51
47
|
},
|
|
52
48
|
"peerDependencies": {
|
|
53
|
-
"@clov-std/kv-store": "^2.0.
|
|
49
|
+
"@clov-std/kv-store": "^2.0.4",
|
|
54
50
|
"elysia": "next",
|
|
55
51
|
"typebox": "^1.3.36"
|
|
56
52
|
}
|