@harshror77/rate-limiter-sdk 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 +36 -0
- package/package.json +9 -0
- package/src/RateLimiterClient.js +26 -0
- package/src/RateLimiterMiddleware.js +29 -0
- package/src/index.js +2 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @rate-limiter/sdk
|
|
2
|
+
|
|
3
|
+
Client and Express middleware for the Rate Limiter as a Service.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { RateLimiterMiddleware } from '@rate-limiter/sdk';
|
|
9
|
+
|
|
10
|
+
app.use(RateLimiterMiddleware({
|
|
11
|
+
serviceUrl: 'http://localhost:3000',
|
|
12
|
+
apiKey: 'your-service-api-key',
|
|
13
|
+
}));
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Every route registered after this middleware is automatically rate limited. If the limit is exceeded, the middleware responds with `429` and an `X-RateLimit-Remaining` header — your route handler never runs.
|
|
17
|
+
|
|
18
|
+
## Using the client directly
|
|
19
|
+
|
|
20
|
+
For non-Express use cases, or to check a limit without blocking a route:
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
import { RateLimiterClient } from '@rate-limiter/sdk';
|
|
24
|
+
|
|
25
|
+
const client = new RateLimiterClient({
|
|
26
|
+
serviceUrl: 'http://localhost:3000',
|
|
27
|
+
apiKey: 'your-service-api-key',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const result = await client.check({ ip: '1.2.3.4', userId: 'abc123' });
|
|
31
|
+
console.log(result.allowed, result.remaining);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Behavior when the rate limiter service is unreachable
|
|
35
|
+
|
|
36
|
+
The middleware fails open — if the rate limiter service itself is down or unreachable, requests are allowed through rather than blocking your entire app. This is a deliberate trade-off: an unrelated service outage shouldn't take down everything that depends on it.
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
export class RateLimiterClient{
|
|
4
|
+
constructor({serviceUrl, apiKey, timeout=3000}){
|
|
5
|
+
this.serviceUrl = serviceUrl;
|
|
6
|
+
this.apiKey = apiKey;
|
|
7
|
+
this.http = axios.create({
|
|
8
|
+
baseURL:serviceUrl,
|
|
9
|
+
timeout
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async check(request={}){
|
|
14
|
+
try{
|
|
15
|
+
const response = await this.http.post('/api/check', request,{
|
|
16
|
+
headers:{'x-api-key':this.apiKey},
|
|
17
|
+
})
|
|
18
|
+
return response.data;
|
|
19
|
+
}catch(err){
|
|
20
|
+
if(err.response){
|
|
21
|
+
return err.response.data;
|
|
22
|
+
}
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { RateLimiterClient } from './RateLimiterClient.js';
|
|
2
|
+
|
|
3
|
+
export function RateLimiterMiddleware({ serviceUrl, apiKey, timeout }) {
|
|
4
|
+
const client = new RateLimiterClient({ serviceUrl, apiKey, timeout });
|
|
5
|
+
|
|
6
|
+
return async function (req, res, next) {
|
|
7
|
+
try {
|
|
8
|
+
const result = await client.check({
|
|
9
|
+
ip: req.ip,
|
|
10
|
+
userId: req.userId,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!result.allowed) {
|
|
14
|
+
res.setHeader('X-RateLimit-Remaining', result.remaining ?? 0);
|
|
15
|
+
return res.status(429).json({
|
|
16
|
+
error: 'Rate limit exceeded',
|
|
17
|
+
deniedBy: result.deniedBy,
|
|
18
|
+
remaining: result.remaining,
|
|
19
|
+
resetAt: result.resetAt,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
res.setHeader('X-RateLimit-Remaining', result.remaining ?? 0);
|
|
24
|
+
next();
|
|
25
|
+
} catch (err) {
|
|
26
|
+
next();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/index.js
ADDED