express-rate-limit 6.0.5 → 6.3.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/changelog.md +31 -0
- package/dist/index.cjs +27 -16
- package/dist/index.d.ts +141 -45
- package/dist/index.mjs +26 -15
- package/package.json +12 -12
- package/readme.md +171 -213
package/changelog.md
CHANGED
|
@@ -6,6 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to
|
|
7
7
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [6.2.1](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.2.1)
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Use the default value for an option when `undefined` is passed to the rate
|
|
14
|
+
limiter.
|
|
15
|
+
|
|
16
|
+
## [6.2.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.2.0)
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Export the `MemoryStore`, so it can now be imported as a named import
|
|
21
|
+
(`import { MemoryStore } from 'express-rate-limit'`).
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Deprecate the `onLimitReached` option (this was supposed to be deprecated in
|
|
26
|
+
v6.0.0 itself); developers should use a custom handler function that checks if
|
|
27
|
+
the rate limit has been exceeded instead.
|
|
28
|
+
|
|
29
|
+
## [6.1.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.1.0)
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- Added a named export `rateLimit` in case the default import does not work.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Added a named export `default`, so Typescript CommonJS developers can
|
|
38
|
+
default-import the library (`import rateLimit from 'express-rate-limit'`).
|
|
39
|
+
|
|
9
40
|
## [6.0.5](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.5)
|
|
10
41
|
|
|
11
42
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,9 @@ var __toCommonJS = /* @__PURE__ */ ((cache) => {
|
|
|
24
24
|
// source/index.ts
|
|
25
25
|
var source_exports = {};
|
|
26
26
|
__export(source_exports, {
|
|
27
|
-
|
|
27
|
+
MemoryStore: () => MemoryStore,
|
|
28
|
+
default: () => lib_default,
|
|
29
|
+
rateLimit: () => lib_default
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
// source/memory-store.ts
|
|
@@ -46,7 +48,8 @@ var MemoryStore = class {
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
async increment(key) {
|
|
49
|
-
|
|
51
|
+
var _a;
|
|
52
|
+
const totalHits = ((_a = this.hits[key]) != null ? _a : 0) + 1;
|
|
50
53
|
this.hits[key] = totalHits;
|
|
51
54
|
return {
|
|
52
55
|
totalHits,
|
|
@@ -99,14 +102,15 @@ var promisifyStore = (passedStore) => {
|
|
|
99
102
|
return new PromisifiedStore();
|
|
100
103
|
};
|
|
101
104
|
var parseOptions = (passedOptions) => {
|
|
102
|
-
|
|
105
|
+
var _a, _b, _c;
|
|
106
|
+
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
107
|
+
const config = {
|
|
103
108
|
windowMs: 60 * 1e3,
|
|
104
|
-
store: new MemoryStore(),
|
|
105
109
|
max: 5,
|
|
106
110
|
message: "Too many requests, please try again later.",
|
|
107
111
|
statusCode: 429,
|
|
108
|
-
legacyHeaders: passedOptions.headers
|
|
109
|
-
standardHeaders: passedOptions.draft_polli_ratelimit_headers
|
|
112
|
+
legacyHeaders: (_a = passedOptions.headers) != null ? _a : true,
|
|
113
|
+
standardHeaders: (_b = passedOptions.draft_polli_ratelimit_headers) != null ? _b : false,
|
|
110
114
|
requestPropertyName: "rateLimit",
|
|
111
115
|
skipFailedRequests: false,
|
|
112
116
|
skipSuccessfulRequests: false,
|
|
@@ -119,17 +123,17 @@ var parseOptions = (passedOptions) => {
|
|
|
119
123
|
return request.ip;
|
|
120
124
|
},
|
|
121
125
|
handler: (_request, response, _next, _optionsUsed) => {
|
|
122
|
-
response.status(
|
|
126
|
+
response.status(config.statusCode).send(config.message);
|
|
123
127
|
},
|
|
124
128
|
onLimitReached: (_request, _response, _optionsUsed) => {
|
|
125
129
|
},
|
|
126
|
-
...
|
|
130
|
+
...notUndefinedOptions,
|
|
131
|
+
store: promisifyStore((_c = notUndefinedOptions.store) != null ? _c : new MemoryStore())
|
|
127
132
|
};
|
|
128
|
-
if (typeof
|
|
133
|
+
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || typeof config.store.resetAll !== "undefined" && typeof config.store.resetAll !== "function" || typeof config.store.init !== "undefined" && typeof config.store.init !== "function") {
|
|
129
134
|
throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");
|
|
130
135
|
}
|
|
131
|
-
|
|
132
|
-
return options;
|
|
136
|
+
return config;
|
|
133
137
|
};
|
|
134
138
|
var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
135
139
|
try {
|
|
@@ -139,7 +143,7 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
|
139
143
|
}
|
|
140
144
|
};
|
|
141
145
|
var rateLimit = (passedOptions) => {
|
|
142
|
-
const options = parseOptions(passedOptions
|
|
146
|
+
const options = parseOptions(passedOptions != null ? passedOptions : {});
|
|
143
147
|
if (typeof options.store.init === "function")
|
|
144
148
|
options.store.init(options);
|
|
145
149
|
const middleware = handleAsyncErrors(async (request, response, next) => {
|
|
@@ -218,9 +222,16 @@ var rateLimit = (passedOptions) => {
|
|
|
218
222
|
middleware.resetKey = options.store.resetKey.bind(options.store);
|
|
219
223
|
return middleware;
|
|
220
224
|
};
|
|
225
|
+
var omitUndefinedOptions = (passedOptions) => {
|
|
226
|
+
const omittedOptions = {};
|
|
227
|
+
for (const k of Object.keys(passedOptions)) {
|
|
228
|
+
const key = k;
|
|
229
|
+
if (passedOptions[key] !== void 0) {
|
|
230
|
+
omittedOptions[key] = passedOptions[key];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return omittedOptions;
|
|
234
|
+
};
|
|
221
235
|
var lib_default = rateLimit;
|
|
222
|
-
|
|
223
|
-
// source/index.ts
|
|
224
|
-
var source_default = lib_default;
|
|
225
236
|
module.exports = __toCommonJS(source_exports);
|
|
226
|
-
module.exports = rateLimit;
|
|
237
|
+
module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v6.
|
|
1
|
+
// Generated by dts-bundle-generator v6.4.0
|
|
2
2
|
|
|
3
3
|
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Callback that fires when a client's hit counter is incremented.
|
|
7
7
|
*
|
|
8
|
-
* @param error {Error | undefined} - The error that occurred, if any
|
|
9
|
-
* @param totalHits {number} - The number of hits for that client so far
|
|
10
|
-
* @param resetTime {Date | undefined} - The time when the counter resets
|
|
8
|
+
* @param error {Error | undefined} - The error that occurred, if any.
|
|
9
|
+
* @param totalHits {number} - The number of hits for that client so far.
|
|
10
|
+
* @param resetTime {Date | undefined} - The time when the counter resets.
|
|
11
11
|
*/
|
|
12
12
|
export declare type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
|
|
13
13
|
/**
|
|
14
14
|
* Method (in the form of middleware) to generate/retrieve a value based on the
|
|
15
|
-
* incoming request
|
|
15
|
+
* incoming request.
|
|
16
16
|
*
|
|
17
|
-
* @param request {Request} - The Express request object
|
|
18
|
-
* @param response {Response} - The Express response object
|
|
17
|
+
* @param request {Request} - The Express request object.
|
|
18
|
+
* @param response {Response} - The Express response object.
|
|
19
19
|
*
|
|
20
|
-
* @returns {T} - The value needed
|
|
20
|
+
* @returns {T} - The value needed.
|
|
21
21
|
*/
|
|
22
22
|
export declare type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
|
|
23
23
|
/**
|
|
24
24
|
* Express request handler that sends back a response when a client is
|
|
25
25
|
* rate-limited.
|
|
26
26
|
*
|
|
27
|
-
* @param request {Request} - The Express request object
|
|
28
|
-
* @param response {Response} - The Express response object
|
|
29
|
-
* @param next {NextFunction} - The Express `next` function, can be called to skip responding
|
|
30
|
-
* @param optionsUsed {Options} - The options used to set up the middleware
|
|
27
|
+
* @param request {Request} - The Express request object.
|
|
28
|
+
* @param response {Response} - The Express response object.
|
|
29
|
+
* @param next {NextFunction} - The Express `next` function, can be called to skip responding.
|
|
30
|
+
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
31
31
|
*/
|
|
32
32
|
export declare type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
|
|
33
33
|
/**
|
|
@@ -35,16 +35,16 @@ export declare type RateLimitExceededEventHandler = (request: Request, response:
|
|
|
35
35
|
* but not for subsequent requests. May be used for logging, etc. Should *not*
|
|
36
36
|
* send a response.
|
|
37
37
|
*
|
|
38
|
-
* @param request {Request} - The Express request object
|
|
39
|
-
* @param response {Response} - The Express response object
|
|
40
|
-
* @param optionsUsed {Options} - The options used to set up the middleware
|
|
38
|
+
* @param request {Request} - The Express request object.
|
|
39
|
+
* @param response {Response} - The Express response object.
|
|
40
|
+
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
41
41
|
*/
|
|
42
42
|
export declare type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
|
|
43
43
|
/**
|
|
44
44
|
* Data returned from the `Store` when a client's hit counter is incremented.
|
|
45
45
|
*
|
|
46
|
-
* @property totalHits {number} - The number of hits for that client so far
|
|
47
|
-
* @property resetTime {Date | undefined} - The time when the counter resets
|
|
46
|
+
* @property totalHits {number} - The number of hits for that client so far.
|
|
47
|
+
* @property resetTime {Date | undefined} - The time when the counter resets.
|
|
48
48
|
*/
|
|
49
49
|
export declare type IncrementResponse = {
|
|
50
50
|
totalHits: number;
|
|
@@ -57,7 +57,7 @@ export declare type RateLimitRequestHandler = RequestHandler & {
|
|
|
57
57
|
/**
|
|
58
58
|
* Method to reset a client's hit counter.
|
|
59
59
|
*
|
|
60
|
-
* @param key {string} - The identifier for a client
|
|
60
|
+
* @param key {string} - The identifier for a client.
|
|
61
61
|
*/
|
|
62
62
|
resetKey: (key: string) => void;
|
|
63
63
|
};
|
|
@@ -70,20 +70,20 @@ export interface LegacyStore {
|
|
|
70
70
|
/**
|
|
71
71
|
* Method to increment a client's hit counter.
|
|
72
72
|
*
|
|
73
|
-
* @param key {string} - The identifier for a client
|
|
74
|
-
* @param callback {IncrementCallback} - The callback to call once the counter is incremented
|
|
73
|
+
* @param key {string} - The identifier for a client.
|
|
74
|
+
* @param callback {IncrementCallback} - The callback to call once the counter is incremented.
|
|
75
75
|
*/
|
|
76
76
|
incr: (key: string, callback: IncrementCallback) => void;
|
|
77
77
|
/**
|
|
78
78
|
* Method to decrement a client's hit counter.
|
|
79
79
|
*
|
|
80
|
-
* @param key {string} - The identifier for a client
|
|
80
|
+
* @param key {string} - The identifier for a client.
|
|
81
81
|
*/
|
|
82
82
|
decrement: (key: string) => void;
|
|
83
83
|
/**
|
|
84
84
|
* Method to reset a client's hit counter.
|
|
85
85
|
*
|
|
86
|
-
* @param key {string} - The identifier for a client
|
|
86
|
+
* @param key {string} - The identifier for a client.
|
|
87
87
|
*/
|
|
88
88
|
resetKey: (key: string) => void;
|
|
89
89
|
/**
|
|
@@ -99,27 +99,27 @@ export interface Store {
|
|
|
99
99
|
* Method that initializes the store, and has access to the options passed to
|
|
100
100
|
* the middleware too.
|
|
101
101
|
*
|
|
102
|
-
* @param options {Options} - The options used to setup the middleware
|
|
102
|
+
* @param options {Options} - The options used to setup the middleware.
|
|
103
103
|
*/
|
|
104
104
|
init?: (options: Options) => void;
|
|
105
105
|
/**
|
|
106
106
|
* Method to increment a client's hit counter.
|
|
107
107
|
*
|
|
108
|
-
* @param key {string} - The identifier for a client
|
|
108
|
+
* @param key {string} - The identifier for a client.
|
|
109
109
|
*
|
|
110
|
-
* @returns {IncrementResponse} - The number of hits and reset time for that client
|
|
110
|
+
* @returns {IncrementResponse} - The number of hits and reset time for that client.
|
|
111
111
|
*/
|
|
112
112
|
increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
|
|
113
113
|
/**
|
|
114
114
|
* Method to decrement a client's hit counter.
|
|
115
115
|
*
|
|
116
|
-
* @param key {string} - The identifier for a client
|
|
116
|
+
* @param key {string} - The identifier for a client.
|
|
117
117
|
*/
|
|
118
118
|
decrement: (key: string) => Promise<void> | void;
|
|
119
119
|
/**
|
|
120
120
|
* Method to reset a client's hit counter.
|
|
121
121
|
*
|
|
122
|
-
* @param key {string} - The identifier for a client
|
|
122
|
+
* @param key {string} - The identifier for a client.
|
|
123
123
|
*/
|
|
124
124
|
resetKey: (key: string) => Promise<void> | void;
|
|
125
125
|
/**
|
|
@@ -133,18 +133,24 @@ export interface Store {
|
|
|
133
133
|
export interface Options {
|
|
134
134
|
/**
|
|
135
135
|
* How long we should remember the requests.
|
|
136
|
+
*
|
|
137
|
+
* Defaults to `60000` ms (= 1 minute).
|
|
136
138
|
*/
|
|
137
139
|
readonly windowMs: number;
|
|
138
140
|
/**
|
|
139
|
-
* The maximum number of
|
|
141
|
+
* The maximum number of connections to allow during the `window` before
|
|
140
142
|
* rate limiting the client.
|
|
141
143
|
*
|
|
142
144
|
* Can be the limit itself as a number or express middleware that parses
|
|
143
145
|
* the request and then figures out the limit.
|
|
146
|
+
*
|
|
147
|
+
* Defaults to `5`.
|
|
144
148
|
*/
|
|
145
149
|
readonly max: number | ValueDeterminingMiddleware<number>;
|
|
146
150
|
/**
|
|
147
151
|
* The response body to send back when a client is rate limited.
|
|
152
|
+
*
|
|
153
|
+
* Defaults to `'Too many requests, please try again later.'`
|
|
148
154
|
*/
|
|
149
155
|
readonly message: any;
|
|
150
156
|
/**
|
|
@@ -156,10 +162,14 @@ export interface Options {
|
|
|
156
162
|
/**
|
|
157
163
|
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
158
164
|
* of requests.
|
|
165
|
+
*
|
|
166
|
+
* Defaults to `true` (for backward compatibility).
|
|
159
167
|
*/
|
|
160
168
|
readonly legacyHeaders: boolean;
|
|
161
169
|
/**
|
|
162
|
-
* Whether to enable support for the rate limit
|
|
170
|
+
* Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
|
|
171
|
+
*
|
|
172
|
+
* Defaults to `false` (for backward compatibility, but its use is recommended).
|
|
163
173
|
*/
|
|
164
174
|
readonly standardHeaders: boolean;
|
|
165
175
|
/**
|
|
@@ -171,43 +181,59 @@ export interface Options {
|
|
|
171
181
|
/**
|
|
172
182
|
* If `true`, the library will (by default) skip all requests that have a 4XX
|
|
173
183
|
* or 5XX status.
|
|
184
|
+
*
|
|
185
|
+
* Defaults to `false`.
|
|
174
186
|
*/
|
|
175
187
|
readonly skipFailedRequests: boolean;
|
|
176
188
|
/**
|
|
177
189
|
* If `true`, the library will (by default) skip all requests that have a
|
|
178
190
|
* status code less than 400.
|
|
191
|
+
*
|
|
192
|
+
* Defaults to `false`.
|
|
179
193
|
*/
|
|
180
194
|
readonly skipSuccessfulRequests: boolean;
|
|
181
|
-
/**
|
|
182
|
-
* Method to determine whether or not the request counts as 'succesful'. Used
|
|
183
|
-
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
|
|
184
|
-
*/
|
|
185
|
-
readonly requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
|
|
186
195
|
/**
|
|
187
196
|
* Method to generate custom identifiers for clients.
|
|
188
197
|
*
|
|
189
198
|
* By default, the client's IP address is used.
|
|
190
199
|
*/
|
|
191
200
|
readonly keyGenerator: ValueDeterminingMiddleware<string>;
|
|
192
|
-
/**
|
|
193
|
-
* Method (in the form of middleware) to determine whether or not this request
|
|
194
|
-
* counts towards a client's quota.
|
|
195
|
-
*/
|
|
196
|
-
readonly skip: ValueDeterminingMiddleware<boolean>;
|
|
197
201
|
/**
|
|
198
202
|
* Express request handler that sends back a response when a client is
|
|
199
203
|
* rate-limited.
|
|
204
|
+
*
|
|
205
|
+
* By default, sends back the `statusCode` and `message` set via the options.
|
|
200
206
|
*/
|
|
201
207
|
readonly handler: RateLimitExceededEventHandler;
|
|
202
208
|
/**
|
|
203
209
|
* Express request handler that sends back a response when a client has
|
|
204
210
|
* reached their rate limit, and will be rate limited on their next request.
|
|
211
|
+
*
|
|
212
|
+
* @deprecated 6.x - Please use a custom `handler` that checks the number of
|
|
213
|
+
* hits instead.
|
|
205
214
|
*/
|
|
206
215
|
readonly onLimitReached: RateLimitReachedEventHandler;
|
|
207
216
|
/**
|
|
208
|
-
*
|
|
217
|
+
* Method (in the form of middleware) to determine whether or not this request
|
|
218
|
+
* counts towards a client's quota.
|
|
219
|
+
*
|
|
220
|
+
* By default, skips no requests.
|
|
221
|
+
*/
|
|
222
|
+
readonly skip: ValueDeterminingMiddleware<boolean>;
|
|
223
|
+
/**
|
|
224
|
+
* Method to determine whether or not the request counts as 'succesful'. Used
|
|
225
|
+
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
|
|
226
|
+
*
|
|
227
|
+
* By default, requests with a response status code less than 400 are considered
|
|
228
|
+
* successful.
|
|
209
229
|
*/
|
|
210
|
-
|
|
230
|
+
readonly requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
|
|
231
|
+
/**
|
|
232
|
+
* The `Store` to use to store the hit count for each client.
|
|
233
|
+
*
|
|
234
|
+
* By default, the built-in `MemoryStore` will be used.
|
|
235
|
+
*/
|
|
236
|
+
store: Store | LegacyStore;
|
|
211
237
|
/**
|
|
212
238
|
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
213
239
|
* of requests.
|
|
@@ -240,9 +266,79 @@ export interface RateLimitInfo {
|
|
|
240
266
|
readonly remaining: number;
|
|
241
267
|
readonly resetTime: Date | undefined;
|
|
242
268
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
269
|
+
/**
|
|
270
|
+
*
|
|
271
|
+
* Create an instance of IP rate-limiting middleware for Express.
|
|
272
|
+
*
|
|
273
|
+
* @param passedOptions {Options} - Options to configure the rate limiter.
|
|
274
|
+
*
|
|
275
|
+
* @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
|
|
276
|
+
*
|
|
277
|
+
* @public
|
|
278
|
+
*/
|
|
279
|
+
export declare const rateLimit: (passedOptions?: Partial<Options> | undefined) => RateLimitRequestHandler;
|
|
280
|
+
/**
|
|
281
|
+
* A `Store` that stores the hit count for each client in memory.
|
|
282
|
+
*
|
|
283
|
+
* @public
|
|
284
|
+
*/
|
|
285
|
+
export declare class MemoryStore implements Store {
|
|
286
|
+
/**
|
|
287
|
+
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
288
|
+
*/
|
|
289
|
+
windowMs: number;
|
|
290
|
+
/**
|
|
291
|
+
* The map that stores the number of hits for each client in memory.
|
|
292
|
+
*/
|
|
293
|
+
hits: {
|
|
294
|
+
[key: string]: number | undefined;
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* The time at which all hit counts will be reset.
|
|
298
|
+
*/
|
|
299
|
+
resetTime: Date;
|
|
300
|
+
/**
|
|
301
|
+
* Method that initializes the store.
|
|
302
|
+
*
|
|
303
|
+
* @param options {Options} - The options used to setup the middleware.
|
|
304
|
+
*/
|
|
305
|
+
init(options: Options): void;
|
|
306
|
+
/**
|
|
307
|
+
* Method to increment a client's hit counter.
|
|
308
|
+
*
|
|
309
|
+
* @param key {string} - The identifier for a client.
|
|
310
|
+
*
|
|
311
|
+
* @returns {IncrementResponse} - The number of hits and reset time for that client.
|
|
312
|
+
*
|
|
313
|
+
* @public
|
|
314
|
+
*/
|
|
315
|
+
increment(key: string): Promise<IncrementResponse>;
|
|
316
|
+
/**
|
|
317
|
+
* Method to decrement a client's hit counter.
|
|
318
|
+
*
|
|
319
|
+
* @param key {string} - The identifier for a client.
|
|
320
|
+
*
|
|
321
|
+
* @public
|
|
322
|
+
*/
|
|
323
|
+
decrement(key: string): Promise<void>;
|
|
324
|
+
/**
|
|
325
|
+
* Method to reset a client's hit counter.
|
|
326
|
+
*
|
|
327
|
+
* @param key {string} - The identifier for a client.
|
|
328
|
+
*
|
|
329
|
+
* @public
|
|
330
|
+
*/
|
|
331
|
+
resetKey(key: string): Promise<void>;
|
|
332
|
+
/**
|
|
333
|
+
* Method to reset everyone's hit counter.
|
|
334
|
+
*
|
|
335
|
+
* @public
|
|
336
|
+
*/
|
|
337
|
+
resetAll(): Promise<void>;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export {
|
|
341
|
+
rateLimit as default,
|
|
342
|
+
};
|
|
247
343
|
|
|
248
344
|
export {};
|
package/dist/index.mjs
CHANGED
|
@@ -17,7 +17,8 @@ var MemoryStore = class {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
async increment(key) {
|
|
20
|
-
|
|
20
|
+
var _a;
|
|
21
|
+
const totalHits = ((_a = this.hits[key]) != null ? _a : 0) + 1;
|
|
21
22
|
this.hits[key] = totalHits;
|
|
22
23
|
return {
|
|
23
24
|
totalHits,
|
|
@@ -70,14 +71,15 @@ var promisifyStore = (passedStore) => {
|
|
|
70
71
|
return new PromisifiedStore();
|
|
71
72
|
};
|
|
72
73
|
var parseOptions = (passedOptions) => {
|
|
73
|
-
|
|
74
|
+
var _a, _b, _c;
|
|
75
|
+
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
76
|
+
const config = {
|
|
74
77
|
windowMs: 60 * 1e3,
|
|
75
|
-
store: new MemoryStore(),
|
|
76
78
|
max: 5,
|
|
77
79
|
message: "Too many requests, please try again later.",
|
|
78
80
|
statusCode: 429,
|
|
79
|
-
legacyHeaders: passedOptions.headers
|
|
80
|
-
standardHeaders: passedOptions.draft_polli_ratelimit_headers
|
|
81
|
+
legacyHeaders: (_a = passedOptions.headers) != null ? _a : true,
|
|
82
|
+
standardHeaders: (_b = passedOptions.draft_polli_ratelimit_headers) != null ? _b : false,
|
|
81
83
|
requestPropertyName: "rateLimit",
|
|
82
84
|
skipFailedRequests: false,
|
|
83
85
|
skipSuccessfulRequests: false,
|
|
@@ -90,17 +92,17 @@ var parseOptions = (passedOptions) => {
|
|
|
90
92
|
return request.ip;
|
|
91
93
|
},
|
|
92
94
|
handler: (_request, response, _next, _optionsUsed) => {
|
|
93
|
-
response.status(
|
|
95
|
+
response.status(config.statusCode).send(config.message);
|
|
94
96
|
},
|
|
95
97
|
onLimitReached: (_request, _response, _optionsUsed) => {
|
|
96
98
|
},
|
|
97
|
-
...
|
|
99
|
+
...notUndefinedOptions,
|
|
100
|
+
store: promisifyStore((_c = notUndefinedOptions.store) != null ? _c : new MemoryStore())
|
|
98
101
|
};
|
|
99
|
-
if (typeof
|
|
102
|
+
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || typeof config.store.resetAll !== "undefined" && typeof config.store.resetAll !== "function" || typeof config.store.init !== "undefined" && typeof config.store.init !== "function") {
|
|
100
103
|
throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");
|
|
101
104
|
}
|
|
102
|
-
|
|
103
|
-
return options;
|
|
105
|
+
return config;
|
|
104
106
|
};
|
|
105
107
|
var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
106
108
|
try {
|
|
@@ -110,7 +112,7 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
|
110
112
|
}
|
|
111
113
|
};
|
|
112
114
|
var rateLimit = (passedOptions) => {
|
|
113
|
-
const options = parseOptions(passedOptions
|
|
115
|
+
const options = parseOptions(passedOptions != null ? passedOptions : {});
|
|
114
116
|
if (typeof options.store.init === "function")
|
|
115
117
|
options.store.init(options);
|
|
116
118
|
const middleware = handleAsyncErrors(async (request, response, next) => {
|
|
@@ -189,10 +191,19 @@ var rateLimit = (passedOptions) => {
|
|
|
189
191
|
middleware.resetKey = options.store.resetKey.bind(options.store);
|
|
190
192
|
return middleware;
|
|
191
193
|
};
|
|
194
|
+
var omitUndefinedOptions = (passedOptions) => {
|
|
195
|
+
const omittedOptions = {};
|
|
196
|
+
for (const k of Object.keys(passedOptions)) {
|
|
197
|
+
const key = k;
|
|
198
|
+
if (passedOptions[key] !== void 0) {
|
|
199
|
+
omittedOptions[key] = passedOptions[key];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return omittedOptions;
|
|
203
|
+
};
|
|
192
204
|
var lib_default = rateLimit;
|
|
193
|
-
|
|
194
|
-
// source/index.ts
|
|
195
|
-
var source_default = lib_default;
|
|
196
205
|
export {
|
|
197
|
-
|
|
206
|
+
MemoryStore,
|
|
207
|
+
lib_default as default,
|
|
208
|
+
lib_default as rateLimit
|
|
198
209
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nathan Friedly",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"changelog.md"
|
|
47
47
|
],
|
|
48
48
|
"engines": {
|
|
49
|
-
"node": ">=
|
|
49
|
+
"node": ">= 12.9.0"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
52
|
"clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
|
|
53
|
-
"build:cjs": "esbuild --bundle --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = rateLimit;\" source/index.ts",
|
|
54
|
-
"build:esm": "esbuild --bundle --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
53
|
+
"build:cjs": "esbuild --bundle --target=es2019 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;\" source/index.ts",
|
|
54
|
+
"build:esm": "esbuild --bundle --target=es2019 --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
55
55
|
"build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts",
|
|
56
56
|
"compile": "run-s clean build:*",
|
|
57
57
|
"lint:code": "xo --ignore test/external/",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"autofix:rest": "prettier --ignore-path .gitignore --ignore-unknown --write .",
|
|
62
62
|
"autofix": "run-s autofix:*",
|
|
63
63
|
"test:lib": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
|
|
64
|
-
"test:ext": "
|
|
64
|
+
"test:ext": "cd test/external/ && bash run-all-tests",
|
|
65
65
|
"test": "run-s lint test:*",
|
|
66
66
|
"pre-commit": "lint-staged",
|
|
67
67
|
"prepare": "run-s compile && husky install config/husky"
|
|
@@ -73,21 +73,21 @@
|
|
|
73
73
|
"@jest/globals": "^27.4.6",
|
|
74
74
|
"@types/express": "^4.17.13",
|
|
75
75
|
"@types/jest": "^27.4.0",
|
|
76
|
-
"@types/node": "^16.11.
|
|
76
|
+
"@types/node": "^16.11.21",
|
|
77
77
|
"@types/supertest": "^2.0.11",
|
|
78
78
|
"cross-env": "^7.0.3",
|
|
79
79
|
"del-cli": "^4.0.1",
|
|
80
|
-
"dts-bundle-generator": "^6.
|
|
81
|
-
"esbuild": "^0.14.
|
|
80
|
+
"dts-bundle-generator": "^6.4.0",
|
|
81
|
+
"esbuild": "^0.14.12",
|
|
82
82
|
"express": "^4.17.1",
|
|
83
83
|
"husky": "^7.0.4",
|
|
84
84
|
"jest": "^27.4.7",
|
|
85
|
-
"lint-staged": "^12.
|
|
85
|
+
"lint-staged": "^12.2.2",
|
|
86
86
|
"npm-run-all": "^4.1.5",
|
|
87
|
-
"supertest": "^6.
|
|
88
|
-
"ts-jest": "^27.1.
|
|
87
|
+
"supertest": "^6.2.2",
|
|
88
|
+
"ts-jest": "^27.1.3",
|
|
89
89
|
"ts-node": "^10.4.0",
|
|
90
|
-
"typescript": "^4.5.
|
|
90
|
+
"typescript": "^4.5.5",
|
|
91
91
|
"xo": "^0.47.0"
|
|
92
92
|
},
|
|
93
93
|
"xo": {
|
package/readme.md
CHANGED
|
@@ -22,9 +22,9 @@ This module was designed to only handle the basics and didn't even support
|
|
|
22
22
|
external stores initially. These other options all are excellent pieces of
|
|
23
23
|
software and may be more appropriate for some situations:
|
|
24
24
|
|
|
25
|
-
- [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
|
|
26
|
-
- [express-brute](https://www.npmjs.com/package/express-brute)
|
|
27
|
-
- [rate-limiter](https://www.npmjs.com/package/express-limiter)
|
|
25
|
+
- [`rate-limiter-flexible`](https://www.npmjs.com/package/rate-limiter-flexible)
|
|
26
|
+
- [`express-brute`](https://www.npmjs.com/package/express-brute)
|
|
27
|
+
- [`rate-limiter`](https://www.npmjs.com/package/express-limiter)
|
|
28
28
|
|
|
29
29
|
## Installation
|
|
30
30
|
|
|
@@ -139,8 +139,7 @@ app.post('/create-account', createAccountLimiter, (request, response) => {
|
|
|
139
139
|
To use a custom store:
|
|
140
140
|
|
|
141
141
|
```ts
|
|
142
|
-
import rateLimit from 'express-rate-limit'
|
|
143
|
-
import MemoryStore from 'express-rate-limit/memory-store.js'
|
|
142
|
+
import rateLimit, { MemoryStore } from 'express-rate-limit'
|
|
144
143
|
|
|
145
144
|
const apiLimiter = rateLimit({
|
|
146
145
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
@@ -189,64 +188,55 @@ it does.
|
|
|
189
188
|
For more information about the `trust proxy` setting, take a look at the
|
|
190
189
|
[official Express documentation](https://expressjs.com/en/guide/behind-proxies.html).
|
|
191
190
|
|
|
192
|
-
##
|
|
193
|
-
|
|
194
|
-
A `request.rateLimit` property is added to all requests with the `limit`,
|
|
195
|
-
`current`, and `remaining` number of requests and, if the store provides it, a
|
|
196
|
-
`resetTime` Date object. These may be used in your application code to take
|
|
197
|
-
additional actions or inform the user of their status.
|
|
198
|
-
|
|
199
|
-
The property name can be configured with the configuration option
|
|
200
|
-
`requestPropertyName`
|
|
201
|
-
|
|
202
|
-
## Configuration options
|
|
191
|
+
## Configuration
|
|
203
192
|
|
|
204
193
|
### `windowMs`
|
|
205
194
|
|
|
195
|
+
> `number`
|
|
196
|
+
|
|
206
197
|
Time frame for which requests are checked/remembered. Also used in the
|
|
207
198
|
`Retry-After` header when the limit is reached.
|
|
208
199
|
|
|
209
|
-
Note: with
|
|
210
|
-
|
|
211
|
-
|
|
200
|
+
Note: with stores that do not implement the `init` function (see the table in
|
|
201
|
+
the [`stores` section below](#stores)), you may need to configure this value
|
|
202
|
+
twice, once here and once on the store. In some cases the units also differ
|
|
203
|
+
(e.g. seconds vs miliseconds).
|
|
212
204
|
|
|
213
205
|
Defaults to `60000` ms (= 1 minute).
|
|
214
206
|
|
|
215
207
|
### `max`
|
|
216
208
|
|
|
217
|
-
|
|
218
|
-
response.
|
|
209
|
+
> `number | function`
|
|
219
210
|
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
The maximum number of connections to allow during the `window` before rate
|
|
212
|
+
limiting the client.
|
|
222
213
|
|
|
223
|
-
|
|
214
|
+
Can be the limit itself as a number or a (sync/async) function that accepts the
|
|
215
|
+
Express `request` and `response` objects and then returns a number.
|
|
224
216
|
|
|
225
|
-
|
|
217
|
+
Defaults to `5`. Set it to `0` to disable the rate limiter.
|
|
226
218
|
|
|
227
|
-
|
|
228
|
-
import rateLimit from 'express-rate-limit'
|
|
219
|
+
An example of using a function:
|
|
229
220
|
|
|
230
|
-
|
|
221
|
+
```ts
|
|
222
|
+
const isPremium = async (user) => {
|
|
231
223
|
// ...
|
|
232
224
|
}
|
|
233
225
|
|
|
234
226
|
const limiter = rateLimit({
|
|
235
|
-
//
|
|
236
|
-
max: (request, response) => {
|
|
237
|
-
if (isPremium(request)) return 10
|
|
227
|
+
// ...
|
|
228
|
+
max: async (request, response) => {
|
|
229
|
+
if (await isPremium(request.user)) return 10
|
|
238
230
|
else return 5
|
|
239
231
|
},
|
|
240
|
-
// ...
|
|
241
232
|
})
|
|
242
|
-
|
|
243
|
-
// Apply the rate limiting middleware to all requests
|
|
244
|
-
app.use(limiter)
|
|
245
233
|
```
|
|
246
234
|
|
|
247
235
|
### `message`
|
|
248
236
|
|
|
249
|
-
|
|
237
|
+
> `any`
|
|
238
|
+
|
|
239
|
+
The response body to send back when a client is rate limited.
|
|
250
240
|
|
|
251
241
|
May be a `string`, JSON object, or any other value that Express's
|
|
252
242
|
[response.send](https://expressjs.com/en/4x/api.html#response.send) method
|
|
@@ -256,240 +246,208 @@ Defaults to `'Too many requests, please try again later.'`
|
|
|
256
246
|
|
|
257
247
|
### `statusCode`
|
|
258
248
|
|
|
259
|
-
|
|
249
|
+
> `number`
|
|
250
|
+
|
|
251
|
+
The HTTP status code to send back when a client is rate limited.
|
|
260
252
|
|
|
261
|
-
Defaults to `429
|
|
253
|
+
Defaults to `429` (HTTP 429 Too Many Requests - RFC 6585).
|
|
262
254
|
|
|
263
255
|
### `legacyHeaders`
|
|
264
256
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
257
|
+
> `boolean`
|
|
258
|
+
|
|
259
|
+
Whether to send the legacy rate limit headers for the limit
|
|
260
|
+
(`X-RateLimit-Limit`), current usage (`X-RateLimit-Remaining`) and reset time
|
|
261
|
+
(if the store provides it) (`X-RateLimit-Reset`) on all responses. If set to
|
|
262
|
+
`true`, the middleware also sends the `Retry-After` header on all blocked
|
|
263
|
+
requests.
|
|
268
264
|
|
|
269
|
-
Defaults to `true
|
|
265
|
+
Defaults to `true` (for backward compatibility).
|
|
270
266
|
|
|
271
267
|
> Renamed in `6.x` from `headers` to `legacyHeaders`.
|
|
272
268
|
|
|
273
269
|
### `standardHeaders`
|
|
274
270
|
|
|
275
|
-
|
|
276
|
-
[ratelimit standardization draft](https://github.com/ietf-wg-httpapi/ratelimit-headers/blob/main/draft-ietf-httpapi-ratelimit-headers.md)
|
|
277
|
-
adopted by the IETF: `RateLimit-Limit`, `RateLimit-Remaining`, and, if the store
|
|
278
|
-
supports it, `RateLimit-Reset`. May be used in conjunction with, or instead of
|
|
279
|
-
the `legacyHeaders` option.
|
|
271
|
+
> `boolean`
|
|
280
272
|
|
|
281
|
-
|
|
273
|
+
Whether to enable support for headers conforming to the
|
|
274
|
+
[ratelimit standardization draft](https://github.com/ietf-wg-httpapi/ratelimit-headers/blob/main/draft-ietf-httpapi-ratelimit-headers.md)
|
|
275
|
+
adopted by the IETF (`RateLimit-Limit`, `RateLimit-Remaining`, and, if the store
|
|
276
|
+
supports it, `RateLimit-Reset`). If set to `true`, the middleware also sends the
|
|
277
|
+
`Retry-After` header on all blocked requests. May be used in conjunction with,
|
|
278
|
+
or instead of the `legacyHeaders` option.
|
|
282
279
|
|
|
283
|
-
Defaults to `false` (for backward compatibility
|
|
280
|
+
Defaults to `false` (for backward compatibility, but its use is recommended).
|
|
284
281
|
|
|
285
282
|
> Renamed in `6.x` from `draft_polli_ratelimit_headers` to `standardHeaders`.
|
|
286
283
|
|
|
287
|
-
### `
|
|
284
|
+
### `requestPropertyName`
|
|
288
285
|
|
|
289
|
-
|
|
286
|
+
> `string`
|
|
290
287
|
|
|
291
|
-
|
|
288
|
+
The name of the property on the Express `request` object to store the rate limit
|
|
289
|
+
info.
|
|
292
290
|
|
|
293
|
-
|
|
294
|
-
const keyGenerator = (request /*, response*/) => request.ip
|
|
295
|
-
```
|
|
291
|
+
Defaults to `'rateLimit'`.
|
|
296
292
|
|
|
297
|
-
### `
|
|
293
|
+
### `skipFailedRequests`
|
|
294
|
+
|
|
295
|
+
> `boolean`
|
|
296
|
+
|
|
297
|
+
When set to `true`, failed requests won't be counted. Request considered failed
|
|
298
|
+
when the `requestWasSuccessful` option returns `false`. By default, this means
|
|
299
|
+
requests fail when:
|
|
300
|
+
|
|
301
|
+
- the response status >= 400
|
|
302
|
+
- the request was cancelled before last chunk of data was sent (response `close`
|
|
303
|
+
event triggered)
|
|
304
|
+
- the response `error` event was triggered by response
|
|
305
|
+
|
|
306
|
+
(Technically they are counted and then un-counted, so a large number of slow
|
|
307
|
+
requests all at once could still trigger a rate-limit. This may be fixed in a
|
|
308
|
+
future release. PRs welcome!)
|
|
309
|
+
|
|
310
|
+
Defaults to `false`.
|
|
311
|
+
|
|
312
|
+
### `skipSuccessfulRequests`
|
|
313
|
+
|
|
314
|
+
> `boolean`
|
|
315
|
+
|
|
316
|
+
If `true`, the library will (by default) skip all requests that are considered
|
|
317
|
+
'failed' by the `requestWasSuccessful` function. By default, this means requests
|
|
318
|
+
succeed when the response status code < 400.
|
|
319
|
+
|
|
320
|
+
(Technically they are counted and then un-counted, so a large number of slow
|
|
321
|
+
requests all at once could still trigger a rate-limit. This may be fixed in a
|
|
322
|
+
future release. PRs welcome!)
|
|
323
|
+
|
|
324
|
+
Defaults to `false`.
|
|
325
|
+
|
|
326
|
+
### `keyGenerator`
|
|
298
327
|
|
|
299
|
-
|
|
300
|
-
`request` and the `response` objects. The `next` param is available if you need
|
|
301
|
-
to pass to the next middleware/route. Finally, the `options` param has all of
|
|
302
|
-
the options that originally passed in when creating the current limiter and the
|
|
303
|
-
default values for other options.
|
|
328
|
+
> `function`
|
|
304
329
|
|
|
305
|
-
|
|
306
|
-
requests and, if the store provides it, a `resetTime` Date object.
|
|
330
|
+
Method to generate custom identifiers for clients.
|
|
307
331
|
|
|
308
|
-
|
|
332
|
+
Should be a (sync/async) function that accepts the Express `request` and
|
|
333
|
+
`response` objects and then returns a string.
|
|
334
|
+
|
|
335
|
+
By default, the client's IP address is used:
|
|
309
336
|
|
|
310
337
|
```ts
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
338
|
+
const limiter = rateLimit({
|
|
339
|
+
// ...
|
|
340
|
+
keyGenerator: (request, response) => request.ip,
|
|
341
|
+
})
|
|
314
342
|
```
|
|
315
343
|
|
|
316
|
-
### `
|
|
344
|
+
### `handler`
|
|
317
345
|
|
|
318
|
-
|
|
319
|
-
`skipSuccessfulRequests` are set to `true`. May be overridden if, for example, a
|
|
320
|
-
service sends out a 200 status code on errors.
|
|
346
|
+
> `function`
|
|
321
347
|
|
|
322
|
-
|
|
348
|
+
Express request handler that sends back a response when a client is
|
|
349
|
+
rate-limited.
|
|
350
|
+
|
|
351
|
+
By default, sends back the `statusCode` and `message` set via the options:
|
|
323
352
|
|
|
324
353
|
```ts
|
|
325
|
-
const
|
|
354
|
+
const limiter = rateLimit({
|
|
355
|
+
// ...
|
|
356
|
+
handler: (request, response, next, options) =>
|
|
357
|
+
response.status(options.statusCode).send(options.message),
|
|
358
|
+
})
|
|
326
359
|
```
|
|
327
360
|
|
|
328
|
-
### `
|
|
361
|
+
### `onLimitReached`
|
|
329
362
|
|
|
330
|
-
|
|
331
|
-
when:
|
|
363
|
+
> `function`
|
|
332
364
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
- response `error` event was triggered by response
|
|
365
|
+
A (sync/async) function that accepts the Express `request` and `response`
|
|
366
|
+
objects that is called when a client has reached their rate limit, and will be
|
|
367
|
+
rate limited on their next request.
|
|
337
368
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
369
|
+
This method was
|
|
370
|
+
[deprecated in v6](https://github.com/nfriedly/express-rate-limit/releases/v6.0.0) -
|
|
371
|
+
Please use a custom `handler` that checks the number of hits instead.
|
|
341
372
|
|
|
342
|
-
|
|
373
|
+
### `skip`
|
|
343
374
|
|
|
344
|
-
|
|
375
|
+
> `function`
|
|
345
376
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
future release.)
|
|
377
|
+
Function to determine whether or not this request counts towards a client's
|
|
378
|
+
quota. Should be a (sync/async) function that accepts the Express `request` and
|
|
379
|
+
`response` objects and then returns `true` or `false`.
|
|
350
380
|
|
|
351
|
-
|
|
381
|
+
Could also act as an allowlist for certain keys:
|
|
352
382
|
|
|
353
|
-
|
|
383
|
+
```ts
|
|
384
|
+
const allowlist = ['192.168.0.56', '192.168.0.21']
|
|
354
385
|
|
|
355
|
-
|
|
356
|
-
|
|
386
|
+
const limiter = rateLimit({
|
|
387
|
+
// ...
|
|
388
|
+
skip: (request, response) => allowlist.includes(request.ip),
|
|
389
|
+
})
|
|
390
|
+
```
|
|
357
391
|
|
|
358
|
-
|
|
392
|
+
By default, it skips no requests:
|
|
359
393
|
|
|
360
394
|
```ts
|
|
361
|
-
const
|
|
395
|
+
const limiter = rateLimit({
|
|
396
|
+
// ...
|
|
397
|
+
skip: (request, response) => false,
|
|
398
|
+
})
|
|
362
399
|
```
|
|
363
400
|
|
|
364
|
-
### `
|
|
401
|
+
### `requestWasSuccessful`
|
|
365
402
|
|
|
366
|
-
|
|
367
|
-
`request` object.
|
|
403
|
+
> `function`
|
|
368
404
|
|
|
369
|
-
|
|
405
|
+
Method to determine whether or not the request counts as 'succesful'. Used when
|
|
406
|
+
either `skipSuccessfulRequests` or `skipFailedRequests` is set to true. Should
|
|
407
|
+
be a (sync/async) function that accepts the Express `request` and `response`
|
|
408
|
+
objects and then returns `true` or `false`.
|
|
409
|
+
|
|
410
|
+
By default, requests with a response status code less than 400 are considered
|
|
411
|
+
successful:
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
const limiter = rateLimit({
|
|
415
|
+
// ...
|
|
416
|
+
requestWasSuccessful: (request, response) => response.statusCode < 400,
|
|
417
|
+
})
|
|
418
|
+
```
|
|
370
419
|
|
|
371
420
|
### `store`
|
|
372
421
|
|
|
373
|
-
|
|
422
|
+
> `Store`
|
|
374
423
|
|
|
375
|
-
|
|
424
|
+
The `Store` to use to store the hit count for each client.
|
|
376
425
|
|
|
377
|
-
|
|
426
|
+
By default, the [`memory-store`](source/memory-store.ts) is used.
|
|
378
427
|
|
|
379
|
-
|
|
380
|
-
Does not share state when app has multiple processes or servers.
|
|
381
|
-
- [rate-limit-redis](https://npmjs.com/package/rate-limit-redis): A
|
|
382
|
-
[Redis](http://redis.io/)-backed store, more suitable for large or demanding
|
|
383
|
-
deployments.
|
|
384
|
-
- [rate-limit-memcached](https://npmjs.org/package/rate-limit-memcached): A
|
|
385
|
-
[Memcached](https://memcached.org/)-backed store.
|
|
386
|
-
- [rate-limit-mongo](https://www.npmjs.com/package/rate-limit-mongo): A
|
|
387
|
-
[MongoDB](https://www.mongodb.com/)-backed store.
|
|
388
|
-
- [precise-memory-rate-limit](https://www.npmjs.com/package/precise-memory-rate-limit) -
|
|
389
|
-
A memory store similar to the built-in one, except that it stores a distinct
|
|
390
|
-
timestamp for each IP rather than bucketing them together.
|
|
428
|
+
Here is a list of external stores:
|
|
391
429
|
|
|
392
|
-
|
|
393
|
-
|
|
430
|
+
| Name | Description | Legacy/Modern |
|
|
431
|
+
| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------- |
|
|
432
|
+
| [`memory-store`](source/memory-store.ts) | _(default)_ Simple in-memory option. Does not share state when app has multiple processes or servers. | Modern as of v6.0.0 |
|
|
433
|
+
| [`rate-limit-redis`](https://npmjs.com/package/rate-limit-redis) | A [Redis](http://redis.io/)-backed store, more suitable for large or demanding deployments. | Modern as of v3.0.0 |
|
|
434
|
+
| [`rate-limit-memcached`](https://npmjs.org/package/rate-limit-memcached) | A [Memcached](https://memcached.org/)-backed store. | Legacy |
|
|
435
|
+
| [`rate-limit-mongo`](https://www.npmjs.com/package/rate-limit-mongo) | A [MongoDB](https://www.mongodb.com/)-backed store. | Legacy |
|
|
436
|
+
| [`precise-memory-rate-limit`](https://www.npmjs.com/package/precise-memory-rate-limit) | A memory store similar to the built-in one, except that it stores a distinct timestamp for each key. | Legacy |
|
|
394
437
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
Options,
|
|
399
|
-
IncrementResponse,
|
|
400
|
-
} from 'express-rate-limit'
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* A {@link Store} that stores the hit count for each client.
|
|
404
|
-
*
|
|
405
|
-
* @public
|
|
406
|
-
*/
|
|
407
|
-
class SomeStore implements Store {
|
|
408
|
-
/**
|
|
409
|
-
* Some store-specific parameter.
|
|
410
|
-
*/
|
|
411
|
-
customParam!: string
|
|
412
|
-
/**
|
|
413
|
-
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
414
|
-
*/
|
|
415
|
-
windowMs!: number
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* @constructor for {@link SomeStore}. Only required if the user needs to pass
|
|
419
|
-
* some store specific parameters. For example, in a Mongo Store, the user will
|
|
420
|
-
* need to pass the URI, username and password for the Mongo database.
|
|
421
|
-
*
|
|
422
|
-
* @param customParam {string} - Some store-specific parameter.
|
|
423
|
-
*/
|
|
424
|
-
constructor(customParam: string) {
|
|
425
|
-
this.customParam = customParam
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Method that actually initializes the store. Must be synchronous.
|
|
430
|
-
*
|
|
431
|
-
* @param options {Options} - The options used to setup the middleware.
|
|
432
|
-
*
|
|
433
|
-
* @public
|
|
434
|
-
*/
|
|
435
|
-
init(options: Options): void {
|
|
436
|
-
this.windowMs = options.windowMs
|
|
437
|
-
|
|
438
|
-
// ...
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Method to increment a client's hit counter.
|
|
443
|
-
*
|
|
444
|
-
* @param key {string} - The identifier for a client
|
|
445
|
-
*
|
|
446
|
-
* @returns {IncrementResponse} - The number of hits and reset time for that client
|
|
447
|
-
*
|
|
448
|
-
* @public
|
|
449
|
-
*/
|
|
450
|
-
async increment(key: string): Promise<IncrementResponse> {
|
|
451
|
-
// ...
|
|
452
|
-
|
|
453
|
-
return {
|
|
454
|
-
totalHits,
|
|
455
|
-
resetTime,
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Method to decrement a client's hit counter.
|
|
461
|
-
*
|
|
462
|
-
* @param key {string} - The identifier for a client
|
|
463
|
-
*
|
|
464
|
-
* @public
|
|
465
|
-
*/
|
|
466
|
-
async decrement(key: string): Promise<void> {
|
|
467
|
-
// ...
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Method to reset a client's hit counter.
|
|
472
|
-
*
|
|
473
|
-
* @param key {string} - The identifier for a client
|
|
474
|
-
*
|
|
475
|
-
* @public
|
|
476
|
-
*/
|
|
477
|
-
async resetKey(key: string): Promise<void> {
|
|
478
|
-
// ...
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Method to reset everyone's hit counter.
|
|
483
|
-
*
|
|
484
|
-
* @public
|
|
485
|
-
*/
|
|
486
|
-
async resetAll(): Promise<void> {
|
|
487
|
-
// ...
|
|
488
|
-
}
|
|
489
|
-
}
|
|
438
|
+
Take a look at
|
|
439
|
+
[this guide](https://github.com/nfriedly/express-rate-limit/wiki/Creating-Your-Own-Store)
|
|
440
|
+
if you wish to create your own store.
|
|
490
441
|
|
|
491
|
-
|
|
492
|
-
|
|
442
|
+
## Request API
|
|
443
|
+
|
|
444
|
+
A `request.rateLimit` property is added to all requests with the `limit`,
|
|
445
|
+
`current`, and `remaining` number of requests and, if the store provides it, a
|
|
446
|
+
`resetTime` Date object. These may be used in your application code to take
|
|
447
|
+
additional actions or inform the user of their status.
|
|
448
|
+
|
|
449
|
+
The property name can be configured with the configuration option
|
|
450
|
+
`requestPropertyName`.
|
|
493
451
|
|
|
494
452
|
## Instance API
|
|
495
453
|
|