@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.
Files changed (2) hide show
  1. package/README.md +25 -58
  2. 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
- # 🚦 Clov Elysia Rate Limit
5
+ # Clov Elysia Rate Limit
6
6
 
7
- Rate limiting for Elysia routes, guards, and groups, as a macro.
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
- ## Why this package?
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
- This plugin uses Elysia's macro system to add rate limiting to any route, guard, or group.
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
- > **Peer dependencies:** `elysia` (v2) and `typebox` must be installed alongside.
17
+ `elysia` (v2) and `typebox` are peer dependencies.
46
18
 
47
- ## ⚙️ Usage
19
+ ## Usage
48
20
 
49
- ### Basic - rate limit by IP
21
+ ### Basic
50
22
 
51
- The simplest form: pass `limit` (max requests) and `window` (time in seconds). Each client IP gets its own counter.
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
- ### Custom store - Redis
39
+ ### Redis
68
40
 
69
- By default, counters are kept in memory. Pass a `BunRedisStore` (or any `KvStore` adapter) for persistence across restarts and multi-instance deployments.
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
- ### Custom key generation - IP + access token
56
+ ### Per user instead of per IP
85
57
 
86
- Useful for authenticated routes where many users share the same public IP (office, corporate proxy).
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
- > **Tip:** `extractClientIp` is exported if you need it inside your own `keyGenerator`.
81
+ `extractClientIp` is exported if you need it in your own `keyGenerator`.
82
+
83
+ ## Headers and the 429
111
84
 
112
- ## 🧯 Error handling
85
+ Every response gets `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.
113
86
 
114
- When the allowance is exceeded, the plugin throws Elysia's `problem()`, so the `429` is a complete
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`. `title` is derived by Elysia from the
127
- status reason phrase. `detail` carries the live counter state, so it changes per occurrence.
128
- The content type is `application/problem+json`, and the `X-RateLimit-*` headers are set before
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
- Full docs: [https://clovlabs.github.io/std/](https://clovlabs.github.io/std/)
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
- ## ⚖️ License
104
+ ## Docs
138
105
 
139
- MIT — see [LICENSE.md](LICENSE.md).
106
+ API reference: [clovlabs.github.io/std](https://clovlabs.github.io/std/)
140
107
 
141
- ## 📧 Contact
108
+ ## License
142
109
 
143
- Maintained by [Clov](https://github.com/ClovLabs).
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.17",
4
- "description": "Rate limiting plugin for Elysia framework.",
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
- "macro",
10
- "open-source",
11
- "plugin",
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.3"
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.3",
49
+ "@clov-std/kv-store": "^2.0.4",
54
50
  "elysia": "next",
55
51
  "typebox": "^1.3.36"
56
52
  }