express-rate-limit 6.0.3 → 6.2.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 CHANGED
@@ -6,6 +6,58 @@ 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
+
53
+ ## [6.0.3](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.3)
54
+
55
+ ### Changed
56
+
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
59
+ [isn't supported in node.js prior to 14.x](https://node.green/#ES2020-features--nullish-coalescing-operator-----).
60
+
9
61
  ## [6.0.2](https://github.com/nfriedly/express-rate-limit/releases/v6.0.2)
10
62
 
11
63
  ### 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
- default: () => source_default
27
+ MemoryStore: () => MemoryStore,
28
+ default: () => lib_default,
29
+ rateLimit: () => lib_default
28
30
  });
29
31
 
30
32
  // source/memory-store.ts
@@ -219,8 +221,5 @@ var rateLimit = (passedOptions) => {
219
221
  return middleware;
220
222
  };
221
223
  var lib_default = rateLimit;
222
-
223
- // source/index.ts
224
- var source_default = lib_default;
225
224
  module.exports = __toCommonJS(source_exports);
226
- module.exports = rateLimit;
225
+ 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.2.0
1
+ // Generated by dts-bundle-generator v6.4.0
2
2
 
3
- import Express from 'express';
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 {Express.Request} - The Express request object
18
- * @param response {Express.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
- export declare type ValueDeterminingMiddleware<T> = (request: Express.Request, response: Express.Response) => T | Promise<T>;
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 {Express.Request} - The Express request object
28
- * @param response {Express.Response} - The Express response object
29
- * @param next {Express.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
- export declare type RateLimitExceededEventHandler = (request: Express.Request, response: Express.Response, next: Express.NextFunction, optionsUsed: Options) => void;
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 {Express.Request} - The Express request object
39
- * @param response {Express.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
- export declare type RateLimitReachedEventHandler = (request: Express.Request, response: Express.Response, optionsUsed: Options) => void;
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 = Express.RequestHandler & {
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 connection to allow during the `window` before
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 standardization headers (`RateLimit-*`).
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,41 +181,57 @@ 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
- * The {@link Store} to use to store the hit count for each client.
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.
229
+ */
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.
209
235
  */
210
236
  store: Store;
211
237
  /**
@@ -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 = Express.Request & {
256
+ export declare type AugmentedRequest = Request & {
231
257
  [key: string]: RateLimitInfo;
232
258
  };
233
259
  /**
@@ -240,9 +266,81 @@ export interface RateLimitInfo {
240
266
  readonly remaining: number;
241
267
  readonly resetTime: Date | undefined;
242
268
  }
243
- declare const rateLimit: (passedOptions?: (Omit<Partial<Options>, "store"> & {
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?: (Omit<Partial<Options>, "store"> & {
244
280
  store?: LegacyStore | Store | undefined;
245
281
  }) | undefined) => RateLimitRequestHandler;
246
- export default rateLimit;
282
+ /**
283
+ * A `Store` that stores the hit count for each client in memory.
284
+ *
285
+ * @public
286
+ */
287
+ export declare class MemoryStore implements Store {
288
+ /**
289
+ * The duration of time before which all hit counts are reset (in milliseconds).
290
+ */
291
+ windowMs: number;
292
+ /**
293
+ * The map that stores the number of hits for each client in memory.
294
+ */
295
+ hits: {
296
+ [key: string]: number | undefined;
297
+ };
298
+ /**
299
+ * The time at which all hit counts will be reset.
300
+ */
301
+ resetTime: Date;
302
+ /**
303
+ * Method that initializes the store.
304
+ *
305
+ * @param options {Options} - The options used to setup the middleware.
306
+ */
307
+ init(options: Options): void;
308
+ /**
309
+ * Method to increment a client's hit counter.
310
+ *
311
+ * @param key {string} - The identifier for a client.
312
+ *
313
+ * @returns {IncrementResponse} - The number of hits and reset time for that client.
314
+ *
315
+ * @public
316
+ */
317
+ increment(key: string): Promise<IncrementResponse>;
318
+ /**
319
+ * Method to decrement a client's hit counter.
320
+ *
321
+ * @param key {string} - The identifier for a client.
322
+ *
323
+ * @public
324
+ */
325
+ decrement(key: string): Promise<void>;
326
+ /**
327
+ * Method to reset a client's hit counter.
328
+ *
329
+ * @param key {string} - The identifier for a client.
330
+ *
331
+ * @public
332
+ */
333
+ resetKey(key: string): Promise<void>;
334
+ /**
335
+ * Method to reset everyone's hit counter.
336
+ *
337
+ * @public
338
+ */
339
+ resetAll(): Promise<void>;
340
+ }
341
+
342
+ export {
343
+ rateLimit as default,
344
+ };
247
345
 
248
346
  export {};
package/dist/index.mjs CHANGED
@@ -190,9 +190,8 @@ var rateLimit = (passedOptions) => {
190
190
  return middleware;
191
191
  };
192
192
  var lib_default = rateLimit;
193
-
194
- // source/index.ts
195
- var source_default = lib_default;
196
193
  export {
197
- source_default as default
194
+ MemoryStore,
195
+ lib_default as default,
196
+ lib_default as rateLimit
198
197
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-rate-limit",
3
- "version": "6.0.3",
3
+ "version": "6.2.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",
@@ -28,13 +28,15 @@
28
28
  "attack"
29
29
  ],
30
30
  "type": "module",
31
- "types": "./dist/index.d.ts",
32
31
  "exports": {
33
32
  ".": {
34
33
  "import": "./dist/index.mjs",
35
34
  "require": "./dist/index.cjs"
36
35
  }
37
36
  },
37
+ "main": "./dist/index.cjs",
38
+ "module": "./dist/index.mjs",
39
+ "types": "./dist/index.d.ts",
38
40
  "files": [
39
41
  "dist/",
40
42
  "tsconfig.json",
@@ -48,7 +50,7 @@
48
50
  },
49
51
  "scripts": {
50
52
  "clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
51
- "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",
52
54
  "build:esm": "esbuild --bundle --format=esm --outfile=dist/index.mjs source/index.ts",
53
55
  "build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts",
54
56
  "compile": "run-s clean build:*",
@@ -60,7 +62,7 @@
60
62
  "autofix": "run-s autofix:*",
61
63
  "test:lib": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
62
64
  "test:ext": "cd test/external/ && bash run-all-tests",
63
- "test": "npm pack && run-s lint test:*",
65
+ "test": "run-s lint test:*",
64
66
  "pre-commit": "lint-staged",
65
67
  "prepare": "run-s compile && husky install config/husky"
66
68
  },
@@ -68,24 +70,24 @@
68
70
  "express": "^4"
69
71
  },
70
72
  "devDependencies": {
71
- "@jest/globals": "^27.4.2",
73
+ "@jest/globals": "^27.4.6",
72
74
  "@types/express": "^4.17.13",
73
- "@types/jest": "^27.0.3",
74
- "@types/node": "^16.11.17",
75
+ "@types/jest": "^27.4.0",
76
+ "@types/node": "^16.11.21",
75
77
  "@types/supertest": "^2.0.11",
76
78
  "cross-env": "^7.0.3",
77
79
  "del-cli": "^4.0.1",
78
- "dts-bundle-generator": "^6.2.0",
79
- "esbuild": "^0.14.8",
80
+ "dts-bundle-generator": "^6.4.0",
81
+ "esbuild": "^0.14.12",
80
82
  "express": "^4.17.1",
81
83
  "husky": "^7.0.4",
82
- "jest": "^27.4.3",
83
- "lint-staged": "^12.1.2",
84
+ "jest": "^27.4.7",
85
+ "lint-staged": "^12.2.2",
84
86
  "npm-run-all": "^4.1.5",
85
- "supertest": "^6.1.6",
86
- "ts-jest": "^27.1.1",
87
+ "supertest": "^6.2.2",
88
+ "ts-jest": "^27.1.3",
87
89
  "ts-node": "^10.4.0",
88
- "typescript": "^4.5.2",
90
+ "typescript": "^4.5.5",
89
91
  "xo": "^0.47.0"
90
92
  },
91
93
  "xo": {
@@ -97,9 +99,7 @@
97
99
  "@typescript-eslint/consistent-indexed-object-style": [
98
100
  "error",
99
101
  "index-signature"
100
- ],
101
- "import/no-named-as-default-member": 0,
102
- "import/no-cycle": 0
102
+ ]
103
103
  }
104
104
  },
105
105
  "prettier": {
package/readme.md CHANGED
@@ -22,11 +22,11 @@ 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
- ## Install
29
+ ## Installation
30
30
 
31
31
  From the npm registry:
32
32
 
@@ -51,14 +51,21 @@ Replace `{version}` with the version of the package that you want to your, e.g.:
51
51
 
52
52
  ## Usage
53
53
 
54
- This library is provided in ESM as well as CJS forms. To import it in a CJS
55
- project:
54
+ ### Importing
55
+
56
+ This library is provided in ESM as well as CJS forms, and works with both
57
+ Javascript and Typescript projects.
58
+
59
+ **This package requires you to use Node 14 or above.**
60
+
61
+ Import it in a CommonJS project (`type: commonjs` or no `type` field in
62
+ `package.json`) as follows:
56
63
 
57
64
  ```ts
58
65
  const rateLimit = require('express-rate-limit')
59
66
  ```
60
67
 
61
- To import it in a Typescript/ESM project:
68
+ Import it in a ESM project (`type: module` in `package.json`) as follows:
62
69
 
63
70
  ```ts
64
71
  import rateLimit from 'express-rate-limit'
@@ -132,8 +139,7 @@ app.post('/create-account', createAccountLimiter, (request, response) => {
132
139
  To use a custom store:
133
140
 
134
141
  ```ts
135
- import rateLimit from 'express-rate-limit'
136
- import MemoryStore from 'express-rate-limit/memory-store.js'
142
+ import rateLimit, { MemoryStore } from 'express-rate-limit'
137
143
 
138
144
  const apiLimiter = rateLimit({
139
145
  windowMs: 15 * 60 * 1000, // 15 minutes
@@ -182,64 +188,55 @@ it does.
182
188
  For more information about the `trust proxy` setting, take a look at the
183
189
  [official Express documentation](https://expressjs.com/en/guide/behind-proxies.html).
184
190
 
185
- ## Request API
186
-
187
- A `request.rateLimit` property is added to all requests with the `limit`,
188
- `current`, and `remaining` number of requests and, if the store provides it, a
189
- `resetTime` Date object. These may be used in your application code to take
190
- additional actions or inform the user of their status.
191
-
192
- The property name can be configured with the configuration option
193
- `requestPropertyName`
194
-
195
- ## Configuration options
191
+ ## Configuration
196
192
 
197
193
  ### `windowMs`
198
194
 
195
+ > `number`
196
+
199
197
  Time frame for which requests are checked/remembered. Also used in the
200
198
  `Retry-After` header when the limit is reached.
201
199
 
202
- Note: with non-default stores, you may need to configure this value twice, once
203
- here and once on the store. In some cases the units also differ (e.g. seconds vs
204
- miliseconds)
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).
205
204
 
206
205
  Defaults to `60000` ms (= 1 minute).
207
206
 
208
207
  ### `max`
209
208
 
210
- Max number of connections during `windowMs` milliseconds before sending a 429
211
- response.
209
+ > `number | function`
212
210
 
213
- May be a number, or a function that returns a number or a promise. If `max` is a
214
- function, it will be called with `request` and `response` params.
211
+ The maximum number of connections to allow during the `window` before rate
212
+ limiting the client.
215
213
 
216
- Defaults to `5`. Set to `0` to disable.
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.
217
216
 
218
- Example of using a function:
217
+ Defaults to `5`. Set it to `0` to disable the rate limiter.
219
218
 
220
- ```ts
221
- import rateLimit from 'express-rate-limit'
219
+ An example of using a function:
222
220
 
223
- const isPremium = (request) => {
221
+ ```ts
222
+ const isPremium = async (user) => {
224
223
  // ...
225
224
  }
226
225
 
227
226
  const limiter = rateLimit({
228
- // `max` could also be an async function or return a promise
229
- max: (request, response) => {
230
- if (isPremium(request)) return 10
227
+ // ...
228
+ max: async (request, response) => {
229
+ if (await isPremium(request.user)) return 10
231
230
  else return 5
232
231
  },
233
- // ...
234
232
  })
235
-
236
- // Apply the rate limiting middleware to all requests
237
- app.use(limiter)
238
233
  ```
239
234
 
240
235
  ### `message`
241
236
 
242
- Error message sent to user when `max` is exceeded.
237
+ > `any`
238
+
239
+ The response body to send back when a client is rate limited.
243
240
 
244
241
  May be a `string`, JSON object, or any other value that Express's
245
242
  [response.send](https://expressjs.com/en/4x/api.html#response.send) method
@@ -249,240 +246,208 @@ Defaults to `'Too many requests, please try again later.'`
249
246
 
250
247
  ### `statusCode`
251
248
 
252
- HTTP status code returned when `max` is exceeded.
249
+ > `number`
253
250
 
254
- Defaults to `429`.
251
+ The HTTP status code to send back when a client is rate limited.
252
+
253
+ Defaults to `429` (HTTP 429 Too Many Requests - RFC 6585).
255
254
 
256
255
  ### `legacyHeaders`
257
256
 
258
- Enable headers for request limit (`X-RateLimit-Limit`) and current usage
259
- (`X-RateLimit-Remaining`) on all responses and time to wait before retrying
260
- (`Retry-After`) when `max` is exceeded.
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.
261
264
 
262
- Defaults to `true`.
265
+ Defaults to `true` (for backward compatibility).
263
266
 
264
267
  > Renamed in `6.x` from `headers` to `legacyHeaders`.
265
268
 
266
269
  ### `standardHeaders`
267
270
 
268
- Enable headers conforming to the
269
- [ratelimit standardization draft](https://github.com/ietf-wg-httpapi/ratelimit-headers/blob/main/draft-ietf-httpapi-ratelimit-headers.md)
270
- adopted by the IETF: `RateLimit-Limit`, `RateLimit-Remaining`, and, if the store
271
- supports it, `RateLimit-Reset`. May be used in conjunction with, or instead of
272
- the `legacyHeaders` option.
271
+ > `boolean`
273
272
 
274
- This setting also enables the `Retry-After` header when `max` is exceeded.
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.
275
279
 
276
- Defaults to `false` (for backward compatibility), but recommended to use.
280
+ Defaults to `false` (for backward compatibility, but its use is recommended).
277
281
 
278
282
  > Renamed in `6.x` from `draft_polli_ratelimit_headers` to `standardHeaders`.
279
283
 
280
- ### `keyGenerator`
284
+ ### `requestPropertyName`
281
285
 
282
- Function used to generate keys.
286
+ > `string`
283
287
 
284
- Defaults to `request.ip`, similar to this:
288
+ The name of the property on the Express `request` object to store the rate limit
289
+ info.
285
290
 
286
- ```ts
287
- const keyGenerator = (request /*, response*/) => request.ip
288
- ```
291
+ Defaults to `'rateLimit'`.
289
292
 
290
- ### `handler`
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.
291
319
 
292
- The function to handle requests once the max limit is exceeded. It receives the
293
- `request` and the `response` objects. The `next` param is available if you need
294
- to pass to the next middleware/route. Finally, the `options` param has all of
295
- the options that originally passed in when creating the current limiter and the
296
- default values for other options.
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`
297
327
 
298
- The `request.rateLimit` object has `limit`, `current`, and `remaining` number of
299
- requests and, if the store provides it, a `resetTime` Date object.
328
+ > `function`
300
329
 
301
- Defaults to:
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:
302
336
 
303
337
  ```ts
304
- const handler = (request, response, next, options) => {
305
- response.status(options.statusCode).send(options.message)
306
- }
338
+ const limiter = rateLimit({
339
+ // ...
340
+ keyGenerator: (request, response) => request.ip,
341
+ })
307
342
  ```
308
343
 
309
- ### `requestWasSuccessful`
344
+ ### `handler`
345
+
346
+ > `function`
310
347
 
311
- Function that is called when `skipFailedRequests` and/or
312
- `skipSuccessfulRequests` are set to `true`. May be overridden if, for example, a
313
- 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.
314
350
 
315
- Defaults to
351
+ By default, sends back the `statusCode` and `message` set via the options:
316
352
 
317
353
  ```ts
318
- const requestWasSuccessful = (request, response) => response.statusCode < 400
354
+ const limiter = rateLimit({
355
+ // ...
356
+ handler: (request, response, next, options) =>
357
+ response.status(options.statusCode).send(options.message),
358
+ })
319
359
  ```
320
360
 
321
- ### `skipFailedRequests`
361
+ ### `onLimitReached`
322
362
 
323
- When set to `true`, failed requests won't be counted. Request considered failed
324
- when:
363
+ > `function`
325
364
 
326
- - response status >= 400
327
- - requests that were cancelled before last chunk of data was sent (response
328
- `close` event triggered)
329
- - 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.
330
368
 
331
- (Technically they are counted and then un-counted, so a large number of slow
332
- requests all at once could still trigger a rate-limit. This may be fixed in a
333
- future release.)
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.
334
372
 
335
- Defaults to `false`.
373
+ ### `skip`
336
374
 
337
- ### `skipSuccessfulRequests`
375
+ > `function`
338
376
 
339
- When set to `true` successful requests (response status < 400) won't be counted.
340
- (Technically they are counted and then un-counted, so a large number of slow
341
- requests all at once could still trigger a rate-limit. This may be fixed in a
342
- 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`.
343
380
 
344
- Defaults to `false`.
381
+ Could also act as an allowlist for certain keys:
345
382
 
346
- ### `skip`
383
+ ```ts
384
+ const allowlist = ['192.168.0.56', '192.168.0.21']
347
385
 
348
- Function used to skip (whitelist) requests. Returning `true`, or a promise that
349
- resolves with `true`, from the function will skip limiting for that request.
386
+ const limiter = rateLimit({
387
+ // ...
388
+ skip: (request, response) => allowlist.includes(request.ip),
389
+ })
390
+ ```
350
391
 
351
- Defaults to always `false` (count all requests):
392
+ By default, it skips no requests:
352
393
 
353
394
  ```ts
354
- const skip = (/*request, response*/) => false
395
+ const limiter = rateLimit({
396
+ // ...
397
+ skip: (request, response) => false,
398
+ })
355
399
  ```
356
400
 
357
- ### `requestPropertyName`
401
+ ### `requestWasSuccessful`
358
402
 
359
- The name of the property that contains the rate limit information to add to the
360
- `request` object.
403
+ > `function`
361
404
 
362
- Defaults to `rateLimit`.
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
+ ```
363
419
 
364
420
  ### `store`
365
421
 
366
- The storage to use when persisting rate limit attempts.
422
+ > `Store`
367
423
 
368
- By default, the [memory store](source/memory-store.ts) is used.
424
+ The `Store` to use to store the hit count for each client.
369
425
 
370
- Available data stores are:
426
+ By default, the [`memory-store`](source/memory-store.ts) is used.
371
427
 
372
- - [memory-store](source/memory-store.ts): _(default)_ Simple in-memory option.
373
- Does not share state when app has multiple processes or servers.
374
- - [rate-limit-redis](https://npmjs.com/package/rate-limit-redis): A
375
- [Redis](http://redis.io/)-backed store, more suitable for large or demanding
376
- deployments.
377
- - [rate-limit-memcached](https://npmjs.org/package/rate-limit-memcached): A
378
- [Memcached](https://memcached.org/)-backed store.
379
- - [rate-limit-mongo](https://www.npmjs.com/package/rate-limit-mongo): A
380
- [MongoDB](https://www.mongodb.com/)-backed store.
381
- - [precise-memory-rate-limit](https://www.npmjs.com/package/precise-memory-rate-limit) -
382
- A memory store similar to the built-in one, except that it stores a distinct
383
- timestamp for each IP rather than bucketing them together.
428
+ Here is a list of external stores:
384
429
 
385
- You may also create your own store. It must implement the `Store` interface as
386
- follows:
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 |
387
437
 
388
- ```ts
389
- import rateLimit, {
390
- Store,
391
- Options,
392
- IncrementResponse,
393
- } from 'express-rate-limit'
394
-
395
- /**
396
- * A {@link Store} that stores the hit count for each client.
397
- *
398
- * @public
399
- */
400
- class SomeStore implements Store {
401
- /**
402
- * Some store-specific parameter.
403
- */
404
- customParam!: string
405
- /**
406
- * The duration of time before which all hit counts are reset (in milliseconds).
407
- */
408
- windowMs!: number
409
-
410
- /**
411
- * @constructor for {@link SomeStore}. Only required if the user needs to pass
412
- * some store specific parameters. For example, in a Mongo Store, the user will
413
- * need to pass the URI, username and password for the Mongo database.
414
- *
415
- * @param customParam {string} - Some store-specific parameter.
416
- */
417
- constructor(customParam: string) {
418
- this.customParam = customParam
419
- }
420
-
421
- /**
422
- * Method that actually initializes the store. Must be synchronous.
423
- *
424
- * @param options {Options} - The options used to setup the middleware.
425
- *
426
- * @public
427
- */
428
- init(options: Options): void {
429
- this.windowMs = options.windowMs
430
-
431
- // ...
432
- }
433
-
434
- /**
435
- * Method to increment a client's hit counter.
436
- *
437
- * @param key {string} - The identifier for a client
438
- *
439
- * @returns {IncrementResponse} - The number of hits and reset time for that client
440
- *
441
- * @public
442
- */
443
- async increment(key: string): Promise<IncrementResponse> {
444
- // ...
445
-
446
- return {
447
- totalHits,
448
- resetTime,
449
- }
450
- }
451
-
452
- /**
453
- * Method to decrement a client's hit counter.
454
- *
455
- * @param key {string} - The identifier for a client
456
- *
457
- * @public
458
- */
459
- async decrement(key: string): Promise<void> {
460
- // ...
461
- }
462
-
463
- /**
464
- * Method to reset a client's hit counter.
465
- *
466
- * @param key {string} - The identifier for a client
467
- *
468
- * @public
469
- */
470
- async resetKey(key: string): Promise<void> {
471
- // ...
472
- }
473
-
474
- /**
475
- * Method to reset everyone's hit counter.
476
- *
477
- * @public
478
- */
479
- async resetAll(): Promise<void> {
480
- // ...
481
- }
482
- }
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.
483
441
 
484
- export default SomeStore
485
- ```
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`.
486
451
 
487
452
  ## Instance API
488
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
- "esModuleInterop": true
12
- },
13
- "include": ["./source/**/*.ts"],
14
- "exclude": ["./node_modules"]
10
+ "esModuleInterop": true,
11
+ "moduleResolution": "node"
12
+ }
15
13
  }