express-rate-limit 6.0.4 → 6.2.1
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 +46 -2
- package/dist/index.cjs +21 -12
- package/dist/index.d.ts +147 -51
- package/dist/index.mjs +20 -11
- package/package.json +13 -13
- package/readme.md +174 -254
- package/tsconfig.json +5 -7
package/changelog.md
CHANGED
|
@@ -6,12 +6,56 @@ 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.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.2.0)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Export the `MemoryStore`, so it can now be imported as a named import
|
|
14
|
+
(`import { MemoryStore } from 'express-rate-limit'`).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Deprecate the `onLimitReached` option (this was supposed to be deprecated in
|
|
19
|
+
v6.0.0 itself); developers should use a custom handler function that checks if
|
|
20
|
+
the rate limit has been exceeded instead.
|
|
21
|
+
|
|
22
|
+
## [6.1.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.1.0)
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Added a named export `rateLimit` in case the default import does not work.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Added a named export `default`, so Typescript CommonJS developers can
|
|
31
|
+
default-import the library (`import rateLimit from 'express-rate-limit'`).
|
|
32
|
+
|
|
33
|
+
## [6.0.5](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.5)
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Use named imports for ExpressJS types so users do not need to enable the
|
|
38
|
+
`esModuleInterop` flag in their Typescript compiler configuration.
|
|
39
|
+
|
|
40
|
+
## [6.0.4](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.4)
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- Upload the built package as a `.tgz` to GitHub releases.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- Add ` main` and `module` fields to `package.json`. This helps tools such as
|
|
49
|
+
ESLint that do not yet support the `exports` field.
|
|
50
|
+
- Bumped the minimum node.js version in `package-lock.json` to match
|
|
51
|
+
`package.json`
|
|
52
|
+
|
|
9
53
|
## [6.0.3](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.3)
|
|
10
54
|
|
|
11
55
|
### Changed
|
|
12
56
|
|
|
13
|
-
- Bumped minimum Node version from 12.9 to 14.5 because the
|
|
14
|
-
uses the nullish coalescing operator (`??`), which
|
|
57
|
+
- Bumped minimum Node version from 12.9 to 14.5 in `package.json` because the
|
|
58
|
+
transpiled output uses the nullish coalescing operator (`??`), which
|
|
15
59
|
[isn't supported in node.js prior to 14.x](https://node.green/#ES2020-features--nullish-coalescing-operator-----).
|
|
16
60
|
|
|
17
61
|
## [6.0.2](https://github.com/nfriedly/express-rate-limit/releases/v6.0.2)
|
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
|
|
@@ -99,9 +101,9 @@ var promisifyStore = (passedStore) => {
|
|
|
99
101
|
return new PromisifiedStore();
|
|
100
102
|
};
|
|
101
103
|
var parseOptions = (passedOptions) => {
|
|
102
|
-
const
|
|
104
|
+
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
105
|
+
const config = {
|
|
103
106
|
windowMs: 60 * 1e3,
|
|
104
|
-
store: new MemoryStore(),
|
|
105
107
|
max: 5,
|
|
106
108
|
message: "Too many requests, please try again later.",
|
|
107
109
|
statusCode: 429,
|
|
@@ -119,17 +121,17 @@ var parseOptions = (passedOptions) => {
|
|
|
119
121
|
return request.ip;
|
|
120
122
|
},
|
|
121
123
|
handler: (_request, response, _next, _optionsUsed) => {
|
|
122
|
-
response.status(
|
|
124
|
+
response.status(config.statusCode).send(config.message);
|
|
123
125
|
},
|
|
124
126
|
onLimitReached: (_request, _response, _optionsUsed) => {
|
|
125
127
|
},
|
|
126
|
-
...
|
|
128
|
+
...notUndefinedOptions,
|
|
129
|
+
store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore())
|
|
127
130
|
};
|
|
128
|
-
if (typeof
|
|
131
|
+
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
132
|
throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");
|
|
130
133
|
}
|
|
131
|
-
|
|
132
|
-
return options;
|
|
134
|
+
return config;
|
|
133
135
|
};
|
|
134
136
|
var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
135
137
|
try {
|
|
@@ -218,9 +220,16 @@ var rateLimit = (passedOptions) => {
|
|
|
218
220
|
middleware.resetKey = options.store.resetKey.bind(options.store);
|
|
219
221
|
return middleware;
|
|
220
222
|
};
|
|
223
|
+
var omitUndefinedOptions = (passedOptions) => {
|
|
224
|
+
const omittedOptions = {};
|
|
225
|
+
for (const k of Object.keys(passedOptions)) {
|
|
226
|
+
const key = k;
|
|
227
|
+
if (passedOptions[key] !== void 0) {
|
|
228
|
+
omittedOptions[key] = passedOptions[key];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return omittedOptions;
|
|
232
|
+
};
|
|
221
233
|
var lib_default = rateLimit;
|
|
222
|
-
|
|
223
|
-
// source/index.ts
|
|
224
|
-
var source_default = lib_default;
|
|
225
234
|
module.exports = __toCommonJS(source_exports);
|
|
226
|
-
module.exports = rateLimit;
|
|
235
|
+
module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v6.
|
|
1
|
+
// Generated by dts-bundle-generator v6.4.0
|
|
2
2
|
|
|
3
|
-
import
|
|
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 {
|
|
18
|
-
* @param response {
|
|
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
|
-
export declare type ValueDeterminingMiddleware<T> = (request:
|
|
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 {
|
|
28
|
-
* @param response {
|
|
29
|
-
* @param next {
|
|
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
|
-
export declare type RateLimitExceededEventHandler = (request:
|
|
32
|
+
export declare type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
|
|
33
33
|
/**
|
|
34
34
|
* Event callback that is triggered on a client's first request that exceeds the limit
|
|
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 {
|
|
39
|
-
* @param response {
|
|
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
|
-
export declare type RateLimitReachedEventHandler = (request:
|
|
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;
|
|
@@ -53,11 +53,11 @@ export declare type IncrementResponse = {
|
|
|
53
53
|
/**
|
|
54
54
|
* A modified Express request handler with the rate limit functions.
|
|
55
55
|
*/
|
|
56
|
-
export declare type RateLimitRequestHandler =
|
|
56
|
+
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.
|
|
@@ -227,7 +253,7 @@ export interface Options {
|
|
|
227
253
|
* The extended request object that includes information about the client's
|
|
228
254
|
* rate limit.
|
|
229
255
|
*/
|
|
230
|
-
export declare type AugmentedRequest =
|
|
256
|
+
export declare type AugmentedRequest = Request & {
|
|
231
257
|
[key: string]: RateLimitInfo;
|
|
232
258
|
};
|
|
233
259
|
/**
|
|
@@ -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
|
@@ -70,9 +70,9 @@ var promisifyStore = (passedStore) => {
|
|
|
70
70
|
return new PromisifiedStore();
|
|
71
71
|
};
|
|
72
72
|
var parseOptions = (passedOptions) => {
|
|
73
|
-
const
|
|
73
|
+
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
74
|
+
const config = {
|
|
74
75
|
windowMs: 60 * 1e3,
|
|
75
|
-
store: new MemoryStore(),
|
|
76
76
|
max: 5,
|
|
77
77
|
message: "Too many requests, please try again later.",
|
|
78
78
|
statusCode: 429,
|
|
@@ -90,17 +90,17 @@ var parseOptions = (passedOptions) => {
|
|
|
90
90
|
return request.ip;
|
|
91
91
|
},
|
|
92
92
|
handler: (_request, response, _next, _optionsUsed) => {
|
|
93
|
-
response.status(
|
|
93
|
+
response.status(config.statusCode).send(config.message);
|
|
94
94
|
},
|
|
95
95
|
onLimitReached: (_request, _response, _optionsUsed) => {
|
|
96
96
|
},
|
|
97
|
-
...
|
|
97
|
+
...notUndefinedOptions,
|
|
98
|
+
store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore())
|
|
98
99
|
};
|
|
99
|
-
if (typeof
|
|
100
|
+
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
101
|
throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");
|
|
101
102
|
}
|
|
102
|
-
|
|
103
|
-
return options;
|
|
103
|
+
return config;
|
|
104
104
|
};
|
|
105
105
|
var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
106
106
|
try {
|
|
@@ -189,10 +189,19 @@ var rateLimit = (passedOptions) => {
|
|
|
189
189
|
middleware.resetKey = options.store.resetKey.bind(options.store);
|
|
190
190
|
return middleware;
|
|
191
191
|
};
|
|
192
|
+
var omitUndefinedOptions = (passedOptions) => {
|
|
193
|
+
const omittedOptions = {};
|
|
194
|
+
for (const k of Object.keys(passedOptions)) {
|
|
195
|
+
const key = k;
|
|
196
|
+
if (passedOptions[key] !== void 0) {
|
|
197
|
+
omittedOptions[key] = passedOptions[key];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return omittedOptions;
|
|
201
|
+
};
|
|
192
202
|
var lib_default = rateLimit;
|
|
193
|
-
|
|
194
|
-
// source/index.ts
|
|
195
|
-
var source_default = lib_default;
|
|
196
203
|
export {
|
|
197
|
-
|
|
204
|
+
MemoryStore,
|
|
205
|
+
lib_default as default,
|
|
206
|
+
lib_default as rateLimit
|
|
198
207
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.1",
|
|
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",
|
|
@@ -50,7 +50,7 @@
|
|
|
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",
|
|
53
|
+
"build:cjs": "esbuild --bundle --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
54
|
"build:esm": "esbuild --bundle --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:*",
|
|
@@ -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"
|
|
@@ -70,24 +70,24 @@
|
|
|
70
70
|
"express": "^4"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@jest/globals": "^27.4.
|
|
73
|
+
"@jest/globals": "^27.4.6",
|
|
74
74
|
"@types/express": "^4.17.13",
|
|
75
|
-
"@types/jest": "^27.0
|
|
76
|
-
"@types/node": "^16.11.
|
|
75
|
+
"@types/jest": "^27.4.0",
|
|
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
|
-
"jest": "^27.4.
|
|
85
|
-
"lint-staged": "^12.
|
|
84
|
+
"jest": "^27.4.7",
|
|
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
|
|
|
@@ -58,57 +58,19 @@ Javascript and Typescript projects.
|
|
|
58
58
|
|
|
59
59
|
**This package requires you to use Node 14 or above.**
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Import it in a CommonJS project as follows:
|
|
64
|
-
|
|
65
|
-
```ts
|
|
66
|
-
const rateLimit = require('express-rate-limit')
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Import it in a ESM project as follows:
|
|
70
|
-
|
|
71
|
-
```ts
|
|
72
|
-
import rateLimit from 'express-rate-limit'
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
#### Typescript
|
|
76
|
-
|
|
77
|
-
If you are using this library in a Typescript project that outputs CommonJS (no
|
|
78
|
-
`type: module` in `package.json` and `module: commonjs` in `tsconfig.json`), set
|
|
79
|
-
`esModuleInterop` to `true` in the `compilerOptions` of your `tsconfig.json` and
|
|
80
|
-
then import it as follows:
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
import rateLimit from 'express-rate-limit'
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
If you cannot set `esModuleInterop` to true, import it as follows instead:
|
|
61
|
+
Import it in a CommonJS project (`type: commonjs` or no `type` field in
|
|
62
|
+
`package.json`) as follows:
|
|
87
63
|
|
|
88
64
|
```ts
|
|
89
65
|
const rateLimit = require('express-rate-limit')
|
|
90
66
|
```
|
|
91
67
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
import { Store, IncrementResponse, ... } from 'express-rate-limit'
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
If you are using this library in a Typescript project that outputs ESM
|
|
99
|
-
(`type: module` in `package.json` and `module: esnext` in `tsconfig.json`),
|
|
100
|
-
import it as follows:
|
|
68
|
+
Import it in a ESM project (`type: module` in `package.json`) as follows:
|
|
101
69
|
|
|
102
70
|
```ts
|
|
103
71
|
import rateLimit from 'express-rate-limit'
|
|
104
72
|
```
|
|
105
73
|
|
|
106
|
-
And use the following to import any types if you need to:
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
import rateLimit, { Store, IncrementResponse, ... } from 'express-rate-limit'
|
|
110
|
-
```
|
|
111
|
-
|
|
112
74
|
### Examples
|
|
113
75
|
|
|
114
76
|
To use it in an API-only server where the rate-limiter should be applied to all
|
|
@@ -177,8 +139,7 @@ app.post('/create-account', createAccountLimiter, (request, response) => {
|
|
|
177
139
|
To use a custom store:
|
|
178
140
|
|
|
179
141
|
```ts
|
|
180
|
-
import rateLimit from 'express-rate-limit'
|
|
181
|
-
import MemoryStore from 'express-rate-limit/memory-store.js'
|
|
142
|
+
import rateLimit, { MemoryStore } from 'express-rate-limit'
|
|
182
143
|
|
|
183
144
|
const apiLimiter = rateLimit({
|
|
184
145
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
@@ -227,64 +188,55 @@ it does.
|
|
|
227
188
|
For more information about the `trust proxy` setting, take a look at the
|
|
228
189
|
[official Express documentation](https://expressjs.com/en/guide/behind-proxies.html).
|
|
229
190
|
|
|
230
|
-
##
|
|
231
|
-
|
|
232
|
-
A `request.rateLimit` property is added to all requests with the `limit`,
|
|
233
|
-
`current`, and `remaining` number of requests and, if the store provides it, a
|
|
234
|
-
`resetTime` Date object. These may be used in your application code to take
|
|
235
|
-
additional actions or inform the user of their status.
|
|
236
|
-
|
|
237
|
-
The property name can be configured with the configuration option
|
|
238
|
-
`requestPropertyName`
|
|
239
|
-
|
|
240
|
-
## Configuration options
|
|
191
|
+
## Configuration
|
|
241
192
|
|
|
242
193
|
### `windowMs`
|
|
243
194
|
|
|
195
|
+
> `number`
|
|
196
|
+
|
|
244
197
|
Time frame for which requests are checked/remembered. Also used in the
|
|
245
198
|
`Retry-After` header when the limit is reached.
|
|
246
199
|
|
|
247
|
-
Note: with
|
|
248
|
-
|
|
249
|
-
|
|
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).
|
|
250
204
|
|
|
251
205
|
Defaults to `60000` ms (= 1 minute).
|
|
252
206
|
|
|
253
207
|
### `max`
|
|
254
208
|
|
|
255
|
-
|
|
256
|
-
response.
|
|
209
|
+
> `number | function`
|
|
257
210
|
|
|
258
|
-
|
|
259
|
-
|
|
211
|
+
The maximum number of connections to allow during the `window` before rate
|
|
212
|
+
limiting the client.
|
|
260
213
|
|
|
261
|
-
|
|
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.
|
|
262
216
|
|
|
263
|
-
|
|
217
|
+
Defaults to `5`. Set it to `0` to disable the rate limiter.
|
|
264
218
|
|
|
265
|
-
|
|
266
|
-
import rateLimit from 'express-rate-limit'
|
|
219
|
+
An example of using a function:
|
|
267
220
|
|
|
268
|
-
|
|
221
|
+
```ts
|
|
222
|
+
const isPremium = async (user) => {
|
|
269
223
|
// ...
|
|
270
224
|
}
|
|
271
225
|
|
|
272
226
|
const limiter = rateLimit({
|
|
273
|
-
//
|
|
274
|
-
max: (request, response) => {
|
|
275
|
-
if (isPremium(request)) return 10
|
|
227
|
+
// ...
|
|
228
|
+
max: async (request, response) => {
|
|
229
|
+
if (await isPremium(request.user)) return 10
|
|
276
230
|
else return 5
|
|
277
231
|
},
|
|
278
|
-
// ...
|
|
279
232
|
})
|
|
280
|
-
|
|
281
|
-
// Apply the rate limiting middleware to all requests
|
|
282
|
-
app.use(limiter)
|
|
283
233
|
```
|
|
284
234
|
|
|
285
235
|
### `message`
|
|
286
236
|
|
|
287
|
-
|
|
237
|
+
> `any`
|
|
238
|
+
|
|
239
|
+
The response body to send back when a client is rate limited.
|
|
288
240
|
|
|
289
241
|
May be a `string`, JSON object, or any other value that Express's
|
|
290
242
|
[response.send](https://expressjs.com/en/4x/api.html#response.send) method
|
|
@@ -294,240 +246,208 @@ Defaults to `'Too many requests, please try again later.'`
|
|
|
294
246
|
|
|
295
247
|
### `statusCode`
|
|
296
248
|
|
|
297
|
-
|
|
249
|
+
> `number`
|
|
250
|
+
|
|
251
|
+
The HTTP status code to send back when a client is rate limited.
|
|
298
252
|
|
|
299
|
-
Defaults to `429
|
|
253
|
+
Defaults to `429` (HTTP 429 Too Many Requests - RFC 6585).
|
|
300
254
|
|
|
301
255
|
### `legacyHeaders`
|
|
302
256
|
|
|
303
|
-
|
|
304
|
-
(`X-RateLimit-Remaining`) on all responses and time to wait before retrying
|
|
305
|
-
(`Retry-After`) when `max` is exceeded.
|
|
257
|
+
> `boolean`
|
|
306
258
|
|
|
307
|
-
|
|
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.
|
|
264
|
+
|
|
265
|
+
Defaults to `true` (for backward compatibility).
|
|
308
266
|
|
|
309
267
|
> Renamed in `6.x` from `headers` to `legacyHeaders`.
|
|
310
268
|
|
|
311
269
|
### `standardHeaders`
|
|
312
270
|
|
|
313
|
-
|
|
314
|
-
[ratelimit standardization draft](https://github.com/ietf-wg-httpapi/ratelimit-headers/blob/main/draft-ietf-httpapi-ratelimit-headers.md)
|
|
315
|
-
adopted by the IETF: `RateLimit-Limit`, `RateLimit-Remaining`, and, if the store
|
|
316
|
-
supports it, `RateLimit-Reset`. May be used in conjunction with, or instead of
|
|
317
|
-
the `legacyHeaders` option.
|
|
271
|
+
> `boolean`
|
|
318
272
|
|
|
319
|
-
|
|
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.
|
|
320
279
|
|
|
321
|
-
Defaults to `false` (for backward compatibility
|
|
280
|
+
Defaults to `false` (for backward compatibility, but its use is recommended).
|
|
322
281
|
|
|
323
282
|
> Renamed in `6.x` from `draft_polli_ratelimit_headers` to `standardHeaders`.
|
|
324
283
|
|
|
325
|
-
### `
|
|
284
|
+
### `requestPropertyName`
|
|
326
285
|
|
|
327
|
-
|
|
286
|
+
> `string`
|
|
328
287
|
|
|
329
|
-
|
|
288
|
+
The name of the property on the Express `request` object to store the rate limit
|
|
289
|
+
info.
|
|
330
290
|
|
|
331
|
-
|
|
332
|
-
const keyGenerator = (request /*, response*/) => request.ip
|
|
333
|
-
```
|
|
291
|
+
Defaults to `'rateLimit'`.
|
|
334
292
|
|
|
335
|
-
### `
|
|
293
|
+
### `skipFailedRequests`
|
|
336
294
|
|
|
337
|
-
|
|
338
|
-
`request` and the `response` objects. The `next` param is available if you need
|
|
339
|
-
to pass to the next middleware/route. Finally, the `options` param has all of
|
|
340
|
-
the options that originally passed in when creating the current limiter and the
|
|
341
|
-
default values for other options.
|
|
295
|
+
> `boolean`
|
|
342
296
|
|
|
343
|
-
|
|
344
|
-
|
|
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:
|
|
345
300
|
|
|
346
|
-
|
|
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`
|
|
327
|
+
|
|
328
|
+
> `function`
|
|
329
|
+
|
|
330
|
+
Method to generate custom identifiers for clients.
|
|
331
|
+
|
|
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:
|
|
347
336
|
|
|
348
337
|
```ts
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
338
|
+
const limiter = rateLimit({
|
|
339
|
+
// ...
|
|
340
|
+
keyGenerator: (request, response) => request.ip,
|
|
341
|
+
})
|
|
352
342
|
```
|
|
353
343
|
|
|
354
|
-
### `
|
|
344
|
+
### `handler`
|
|
345
|
+
|
|
346
|
+
> `function`
|
|
355
347
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
service sends out a 200 status code on errors.
|
|
348
|
+
Express request handler that sends back a response when a client is
|
|
349
|
+
rate-limited.
|
|
359
350
|
|
|
360
|
-
|
|
351
|
+
By default, sends back the `statusCode` and `message` set via the options:
|
|
361
352
|
|
|
362
353
|
```ts
|
|
363
|
-
const
|
|
354
|
+
const limiter = rateLimit({
|
|
355
|
+
// ...
|
|
356
|
+
handler: (request, response, next, options) =>
|
|
357
|
+
response.status(options.statusCode).send(options.message),
|
|
358
|
+
})
|
|
364
359
|
```
|
|
365
360
|
|
|
366
|
-
### `
|
|
361
|
+
### `onLimitReached`
|
|
367
362
|
|
|
368
|
-
|
|
369
|
-
when:
|
|
363
|
+
> `function`
|
|
370
364
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
- 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.
|
|
375
368
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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.
|
|
379
372
|
|
|
380
|
-
|
|
373
|
+
### `skip`
|
|
381
374
|
|
|
382
|
-
|
|
375
|
+
> `function`
|
|
383
376
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
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`.
|
|
388
380
|
|
|
389
|
-
|
|
381
|
+
Could also act as an allowlist for certain keys:
|
|
390
382
|
|
|
391
|
-
|
|
383
|
+
```ts
|
|
384
|
+
const allowlist = ['192.168.0.56', '192.168.0.21']
|
|
392
385
|
|
|
393
|
-
|
|
394
|
-
|
|
386
|
+
const limiter = rateLimit({
|
|
387
|
+
// ...
|
|
388
|
+
skip: (request, response) => allowlist.includes(request.ip),
|
|
389
|
+
})
|
|
390
|
+
```
|
|
395
391
|
|
|
396
|
-
|
|
392
|
+
By default, it skips no requests:
|
|
397
393
|
|
|
398
394
|
```ts
|
|
399
|
-
const
|
|
395
|
+
const limiter = rateLimit({
|
|
396
|
+
// ...
|
|
397
|
+
skip: (request, response) => false,
|
|
398
|
+
})
|
|
400
399
|
```
|
|
401
400
|
|
|
402
|
-
### `
|
|
401
|
+
### `requestWasSuccessful`
|
|
402
|
+
|
|
403
|
+
> `function`
|
|
403
404
|
|
|
404
|
-
|
|
405
|
-
`
|
|
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`.
|
|
406
409
|
|
|
407
|
-
|
|
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
|
+
```
|
|
408
419
|
|
|
409
420
|
### `store`
|
|
410
421
|
|
|
411
|
-
|
|
422
|
+
> `Store`
|
|
412
423
|
|
|
413
|
-
|
|
424
|
+
The `Store` to use to store the hit count for each client.
|
|
414
425
|
|
|
415
|
-
|
|
426
|
+
By default, the [`memory-store`](source/memory-store.ts) is used.
|
|
416
427
|
|
|
417
|
-
|
|
418
|
-
Does not share state when app has multiple processes or servers.
|
|
419
|
-
- [rate-limit-redis](https://npmjs.com/package/rate-limit-redis): A
|
|
420
|
-
[Redis](http://redis.io/)-backed store, more suitable for large or demanding
|
|
421
|
-
deployments.
|
|
422
|
-
- [rate-limit-memcached](https://npmjs.org/package/rate-limit-memcached): A
|
|
423
|
-
[Memcached](https://memcached.org/)-backed store.
|
|
424
|
-
- [rate-limit-mongo](https://www.npmjs.com/package/rate-limit-mongo): A
|
|
425
|
-
[MongoDB](https://www.mongodb.com/)-backed store.
|
|
426
|
-
- [precise-memory-rate-limit](https://www.npmjs.com/package/precise-memory-rate-limit) -
|
|
427
|
-
A memory store similar to the built-in one, except that it stores a distinct
|
|
428
|
-
timestamp for each IP rather than bucketing them together.
|
|
428
|
+
Here is a list of external stores:
|
|
429
429
|
|
|
430
|
-
|
|
431
|
-
|
|
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 |
|
|
432
437
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
Options,
|
|
437
|
-
IncrementResponse,
|
|
438
|
-
} from 'express-rate-limit'
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* A {@link Store} that stores the hit count for each client.
|
|
442
|
-
*
|
|
443
|
-
* @public
|
|
444
|
-
*/
|
|
445
|
-
class SomeStore implements Store {
|
|
446
|
-
/**
|
|
447
|
-
* Some store-specific parameter.
|
|
448
|
-
*/
|
|
449
|
-
customParam!: string
|
|
450
|
-
/**
|
|
451
|
-
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
452
|
-
*/
|
|
453
|
-
windowMs!: number
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* @constructor for {@link SomeStore}. Only required if the user needs to pass
|
|
457
|
-
* some store specific parameters. For example, in a Mongo Store, the user will
|
|
458
|
-
* need to pass the URI, username and password for the Mongo database.
|
|
459
|
-
*
|
|
460
|
-
* @param customParam {string} - Some store-specific parameter.
|
|
461
|
-
*/
|
|
462
|
-
constructor(customParam: string) {
|
|
463
|
-
this.customParam = customParam
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Method that actually initializes the store. Must be synchronous.
|
|
468
|
-
*
|
|
469
|
-
* @param options {Options} - The options used to setup the middleware.
|
|
470
|
-
*
|
|
471
|
-
* @public
|
|
472
|
-
*/
|
|
473
|
-
init(options: Options): void {
|
|
474
|
-
this.windowMs = options.windowMs
|
|
475
|
-
|
|
476
|
-
// ...
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Method to increment a client's hit counter.
|
|
481
|
-
*
|
|
482
|
-
* @param key {string} - The identifier for a client
|
|
483
|
-
*
|
|
484
|
-
* @returns {IncrementResponse} - The number of hits and reset time for that client
|
|
485
|
-
*
|
|
486
|
-
* @public
|
|
487
|
-
*/
|
|
488
|
-
async increment(key: string): Promise<IncrementResponse> {
|
|
489
|
-
// ...
|
|
490
|
-
|
|
491
|
-
return {
|
|
492
|
-
totalHits,
|
|
493
|
-
resetTime,
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Method to decrement a client's hit counter.
|
|
499
|
-
*
|
|
500
|
-
* @param key {string} - The identifier for a client
|
|
501
|
-
*
|
|
502
|
-
* @public
|
|
503
|
-
*/
|
|
504
|
-
async decrement(key: string): Promise<void> {
|
|
505
|
-
// ...
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Method to reset a client's hit counter.
|
|
510
|
-
*
|
|
511
|
-
* @param key {string} - The identifier for a client
|
|
512
|
-
*
|
|
513
|
-
* @public
|
|
514
|
-
*/
|
|
515
|
-
async resetKey(key: string): Promise<void> {
|
|
516
|
-
// ...
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Method to reset everyone's hit counter.
|
|
521
|
-
*
|
|
522
|
-
* @public
|
|
523
|
-
*/
|
|
524
|
-
async resetAll(): Promise<void> {
|
|
525
|
-
// ...
|
|
526
|
-
}
|
|
527
|
-
}
|
|
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.
|
|
528
441
|
|
|
529
|
-
|
|
530
|
-
|
|
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`.
|
|
531
451
|
|
|
532
452
|
## Instance API
|
|
533
453
|
|
package/tsconfig.json
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
+
"include": ["source/"],
|
|
3
|
+
"exclude": ["node_modules/"],
|
|
2
4
|
"compilerOptions": {
|
|
3
5
|
"declaration": true,
|
|
4
|
-
|
|
5
6
|
"strict": true,
|
|
6
7
|
"noUnusedLocals": true,
|
|
7
8
|
"noImplicitReturns": true,
|
|
8
9
|
"noFallthroughCasesInSwitch": true,
|
|
9
|
-
|
|
10
|
-
"moduleResolution": "node"
|
|
11
|
-
|
|
12
|
-
},
|
|
13
|
-
"include": ["./source/**/*.ts"],
|
|
14
|
-
"exclude": ["./node_modules"]
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"moduleResolution": "node"
|
|
12
|
+
}
|
|
15
13
|
}
|