@nestjs/throttler 4.2.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -159
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/throttler-module-options.interface.d.ts +12 -4
- package/dist/throttler-storage.interface.d.ts +0 -2
- package/dist/throttler-storage.interface.js.map +1 -1
- package/dist/throttler.decorator.d.ts +8 -2
- package/dist/throttler.decorator.js +16 -12
- package/dist/throttler.decorator.js.map +1 -1
- package/dist/throttler.guard.d.ts +12 -5
- package/dist/throttler.guard.interface.d.ts +7 -0
- package/dist/throttler.guard.interface.js +3 -0
- package/dist/throttler.guard.interface.js.map +1 -0
- package/dist/throttler.guard.js +77 -30
- package/dist/throttler.guard.js.map +1 -1
- package/dist/throttler.module.js +3 -2
- package/dist/throttler.module.js.map +1 -1
- package/dist/throttler.providers.d.ts +2 -1
- package/dist/throttler.providers.js +3 -1
- package/dist/throttler.providers.js.map +1 -1
- package/dist/throttler.service.js +3 -2
- package/dist/throttler.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities.d.ts +5 -0
- package/dist/utilities.js +14 -0
- package/dist/utilities.js.map +1 -0
- package/package.json +33 -32
package/README.md
CHANGED
|
@@ -20,8 +20,6 @@
|
|
|
20
20
|
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
|
21
21
|
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
|
22
22
|
</p>
|
|
23
|
-
<!--[](https://opencollective.com/nest#backer)
|
|
24
|
-
[](https://opencollective.com/nest#sponsor)-->
|
|
25
23
|
|
|
26
24
|
## Description
|
|
27
25
|
|
|
@@ -50,185 +48,117 @@ For NestJS v10, please use version 4.1.0 or above
|
|
|
50
48
|
- [Table of Contents](#table-of-contents)
|
|
51
49
|
- [Usage](#usage)
|
|
52
50
|
- [ThrottlerModule](#throttlermodule)
|
|
53
|
-
- [
|
|
54
|
-
|
|
55
|
-
- [@SkipThrottle()](#skipthrottle)
|
|
56
|
-
- [Ignoring specific user agents](#ignoring-specific-user-agents)
|
|
57
|
-
- [ThrottlerStorage](#throttlerstorage)
|
|
51
|
+
- [Customization](#customization)
|
|
52
|
+
- [ThrottlerStorage](#storages)
|
|
58
53
|
- [Proxies](#proxies)
|
|
59
|
-
- [Working with Websockets](#
|
|
60
|
-
- [Working with GraphQL](#
|
|
54
|
+
- [Working with Websockets](#websockets)
|
|
55
|
+
- [Working with GraphQL](#graphql)
|
|
61
56
|
- [Community Storage Providers](#community-storage-providers)
|
|
62
57
|
|
|
63
58
|
## Usage
|
|
64
59
|
|
|
65
60
|
### ThrottlerModule
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
in a synchronous or asynchronous manner. All the needs to be passed is the
|
|
69
|
-
`ttl`, the time to live in seconds for the request tracker, and the `limit`, or
|
|
70
|
-
how many times an endpoint can be hit before returning a 429.
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
import { APP_GUARD } from '@nestjs/core';
|
|
74
|
-
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
|
|
62
|
+
Once the installation is complete, the `ThrottlerModule` can be configured as any other Nest package with `forRoot` or `forRootAsync` methods.
|
|
75
63
|
|
|
64
|
+
```typescript
|
|
65
|
+
@@filename(app.module)
|
|
76
66
|
@Module({
|
|
77
67
|
imports: [
|
|
78
|
-
ThrottlerModule.forRoot({
|
|
79
|
-
ttl:
|
|
68
|
+
ThrottlerModule.forRoot([{
|
|
69
|
+
ttl: 60000,
|
|
80
70
|
limit: 10,
|
|
81
|
-
}),
|
|
82
|
-
],
|
|
83
|
-
providers: [
|
|
84
|
-
{
|
|
85
|
-
provide: APP_GUARD,
|
|
86
|
-
useClass: ThrottlerGuard,
|
|
87
|
-
},
|
|
71
|
+
}]),
|
|
88
72
|
],
|
|
89
73
|
})
|
|
90
74
|
export class AppModule {}
|
|
91
75
|
```
|
|
92
76
|
|
|
93
|
-
The above
|
|
77
|
+
The above will set the global options for the `ttl`, the time to live in milliseconds, and the `limit`, the maximum number of requests within the ttl, for the routes of your application that are guarded.
|
|
94
78
|
|
|
95
|
-
|
|
96
|
-
@Module({
|
|
97
|
-
imports: [
|
|
98
|
-
ThrottlerModule.forRootAsync({
|
|
99
|
-
imports: [ConfigModule],
|
|
100
|
-
inject: [ConfigService],
|
|
101
|
-
useFactory: (config: ConfigService) => ({
|
|
102
|
-
ttl: config.get('THROTTLE_TTL'),
|
|
103
|
-
limit: config.get('THROTTLE_LIMIT'),
|
|
104
|
-
}),
|
|
105
|
-
}),
|
|
106
|
-
],
|
|
107
|
-
providers: [
|
|
108
|
-
{
|
|
109
|
-
provide: APP_GUARD,
|
|
110
|
-
useClass: ThrottlerGuard,
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
})
|
|
114
|
-
export class AppModule {}
|
|
115
|
-
```
|
|
79
|
+
Once the module has been imported, you can then choose how you would like to bind the `ThrottlerGuard`. Any kind of binding as mentioned in the [guards](https://docs.nestjs.com/guards) section is fine. If you wanted to bind the guard globally, for example, you could do so by adding this provider to any module:
|
|
116
80
|
|
|
117
|
-
|
|
81
|
+
```typescript
|
|
82
|
+
{
|
|
83
|
+
provide: APP_GUARD,
|
|
84
|
+
useClass: ThrottlerGuard
|
|
85
|
+
}
|
|
86
|
+
```
|
|
118
87
|
|
|
119
|
-
|
|
120
|
-
then all the incoming requests will be throttled by default. This can also be
|
|
121
|
-
omitted in favor of `@UseGuards(ThrottlerGuard)`. The global guard check can be
|
|
122
|
-
skipped using the `@SkipThrottle()` decorator mentioned later.
|
|
88
|
+
#### Multiple Throttler Definitions
|
|
123
89
|
|
|
124
|
-
|
|
90
|
+
There may come upon times where you want to set up multiple throttling definitions, like no more than 3 calls in a second, 20 calls in 10 seconds, and 100 calls in a minute. To do so, you can set up your definitions in the array with named options, that can later be referenced in the `@SkipThrottle()` and `@Throttle()` decorators to change the options again.
|
|
125
91
|
|
|
126
|
-
```
|
|
127
|
-
|
|
92
|
+
```typescript
|
|
93
|
+
@@filename(app.module)
|
|
128
94
|
@Module({
|
|
129
95
|
imports: [
|
|
130
|
-
ThrottlerModule.forRoot(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
96
|
+
ThrottlerModule.forRoot([
|
|
97
|
+
{
|
|
98
|
+
name: 'short'
|
|
99
|
+
ttl: 1000,
|
|
100
|
+
limit: 3,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'medium',
|
|
104
|
+
ttl: 10000,
|
|
105
|
+
limit: 20
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
long: 'long',
|
|
109
|
+
ttl: 60000,
|
|
110
|
+
limit: 100
|
|
111
|
+
}
|
|
112
|
+
]),
|
|
134
113
|
],
|
|
135
114
|
})
|
|
136
115
|
export class AppModule {}
|
|
137
|
-
|
|
138
|
-
// app.controller.ts
|
|
139
|
-
@Controller()
|
|
140
|
-
export class AppController {
|
|
141
|
-
@UseGuards(ThrottlerGuard)
|
|
142
|
-
@Throttle(5, 30)
|
|
143
|
-
normal() {}
|
|
144
|
-
}
|
|
145
116
|
```
|
|
146
117
|
|
|
147
|
-
|
|
118
|
+
#### Customization
|
|
148
119
|
|
|
149
|
-
|
|
120
|
+
There may be a time where you want to bind the guard to a controller or globally, but want to disable rate limiting for one or more of your endpoints. For that, you can use the `@SkipThrottle()` decorator, to negate the throttler for an entire class or a single route. The `@SkipThrottle()` decorator can also take in an object of string keys with boolean values for if there is a case where you want to exclude _most_ of a controller, but not every route, and configure it per throttler set if you have more than one. If you do not pass an object, the default is to use `{{ '{' }} default: true {{ '}' }}`
|
|
150
121
|
|
|
151
|
-
```
|
|
152
|
-
@
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
This decorator will set `THROTTLER_LIMIT` and `THROTTLER_TTL` metadatas on the
|
|
156
|
-
route, for retrieval from the `Reflector` class. Can be applied to controllers
|
|
157
|
-
and routes.
|
|
158
|
-
|
|
159
|
-
#### @SkipThrottle()
|
|
160
|
-
|
|
161
|
-
```ts
|
|
162
|
-
@SkipThrottle(skip = true)
|
|
122
|
+
```typescript
|
|
123
|
+
@SkipThrottle()
|
|
124
|
+
@Controller('users')
|
|
125
|
+
export class UsersController {}
|
|
163
126
|
```
|
|
164
127
|
|
|
165
|
-
This decorator can be used to skip a route or a class
|
|
166
|
-
skipping of a route in a class that is skipped.
|
|
128
|
+
This `@SkipThrottle()` decorator can be used to skip a route or a class or to negate the skipping of a route in a class that is skipped.
|
|
167
129
|
|
|
168
|
-
```
|
|
130
|
+
```typescript
|
|
169
131
|
@SkipThrottle()
|
|
170
|
-
@Controller()
|
|
171
|
-
export class
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
132
|
+
@Controller('users')
|
|
133
|
+
export class UsersController {
|
|
134
|
+
// Rate limiting is applied to this route.
|
|
135
|
+
@SkipThrottle({ default: false })
|
|
136
|
+
dontSkip() {
|
|
137
|
+
return 'List users work with Rate limiting.';
|
|
138
|
+
}
|
|
139
|
+
// This route will skip rate limiting.
|
|
140
|
+
doSkip() {
|
|
141
|
+
return 'List users work without Rate limiting.';
|
|
142
|
+
}
|
|
176
143
|
}
|
|
177
144
|
```
|
|
178
145
|
|
|
179
|
-
|
|
180
|
-
while `doSkip` would not be limited in any way.
|
|
181
|
-
|
|
182
|
-
### Ignoring specific user agents
|
|
183
|
-
|
|
184
|
-
You can use the `ignoreUserAgents` key to ignore specific user agents.
|
|
185
|
-
|
|
186
|
-
```ts
|
|
187
|
-
@Module({
|
|
188
|
-
imports: [
|
|
189
|
-
ThrottlerModule.forRoot({
|
|
190
|
-
ttl: 60,
|
|
191
|
-
limit: 10,
|
|
192
|
-
ignoreUserAgents: [
|
|
193
|
-
// Don't throttle request that have 'googlebot' defined in them.
|
|
194
|
-
// Example user agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
|
195
|
-
/googlebot/gi,
|
|
196
|
-
|
|
197
|
-
// Don't throttle request that have 'bingbot' defined in them.
|
|
198
|
-
// Example user agent: Mozilla/5.0 (compatible; Bingbot/2.0; +http://www.bing.com/bingbot.htm)
|
|
199
|
-
new RegExp('bingbot', 'gi'),
|
|
200
|
-
],
|
|
201
|
-
}),
|
|
202
|
-
],
|
|
203
|
-
})
|
|
204
|
-
export class AppModule {}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### ThrottlerStorage
|
|
208
|
-
|
|
209
|
-
Interface to define the methods to handle the details when it comes to keeping track of the requests.
|
|
146
|
+
There is also the `@Throttle()` decorator which can be used to override the `limit` and `ttl` set in the global module, to give tighter or looser security options. This decorator can be used on a class or a function as well. With version 5 and onwards, the decorator takes in an object with the string relating to the name of the throttler set, and an object with the limit and ttl keys and integer values, similar to the options passed to the root module. If you do not have a name set in your original options, use the string `default` You have to configure it like this:
|
|
210
147
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
```ts
|
|
219
|
-
export interface ThrottlerStorage {
|
|
220
|
-
storage: Record<string, ThrottlerStorageOptions>;
|
|
221
|
-
increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;
|
|
148
|
+
```typescript
|
|
149
|
+
// Override default configuration for Rate limiting and duration.
|
|
150
|
+
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
|
151
|
+
@Get()
|
|
152
|
+
findAll() {
|
|
153
|
+
return "List users works with custom rate limiting.";
|
|
222
154
|
}
|
|
223
155
|
```
|
|
224
156
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
### Proxies
|
|
157
|
+
#### Proxies
|
|
228
158
|
|
|
229
|
-
If
|
|
159
|
+
If your application runs behind a proxy server, check the specific HTTP adapter options ([express](http://expressjs.com/en/guide/behind-proxies.html) and [fastify](https://www.fastify.io/docs/latest/Reference/Server/#trustproxy)) for the `trust proxy` option and enable it. Doing so will allow you to get the original IP address from the `X-Forwarded-For` header, and you can override the `getTracker()` method to pull the value from the header rather than from `req.ip`. The following example works with both express and fastify:
|
|
230
160
|
|
|
231
|
-
```
|
|
161
|
+
```typescript
|
|
232
162
|
// throttler-behind-proxy.guard.ts
|
|
233
163
|
import { ThrottlerGuard } from '@nestjs/throttler';
|
|
234
164
|
import { Injectable } from '@nestjs/common';
|
|
@@ -242,23 +172,22 @@ export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
|
|
|
242
172
|
|
|
243
173
|
// app.controller.ts
|
|
244
174
|
import { ThrottlerBehindProxyGuard } from './throttler-behind-proxy.guard';
|
|
175
|
+
|
|
245
176
|
@UseGuards(ThrottlerBehindProxyGuard)
|
|
246
177
|
```
|
|
247
178
|
|
|
248
|
-
|
|
179
|
+
> info **Hint** You can find the API of the `req` Request object for express [here](https://expressjs.com/en/api.html#req.ips) and for fastify [here](https://www.fastify.io/docs/latest/Reference/Request/).
|
|
180
|
+
|
|
181
|
+
#### Websockets
|
|
249
182
|
|
|
250
|
-
|
|
183
|
+
This module can work with websockets, but it requires some class extension. You can extend the `ThrottlerGuard` and override the `handleRequest` method like so:
|
|
251
184
|
|
|
252
|
-
```
|
|
185
|
+
```typescript
|
|
253
186
|
@Injectable()
|
|
254
187
|
export class WsThrottlerGuard extends ThrottlerGuard {
|
|
255
188
|
async handleRequest(context: ExecutionContext, limit: number, ttl: number): Promise<boolean> {
|
|
256
189
|
const client = context.switchToWs().getClient();
|
|
257
|
-
|
|
258
|
-
const ip = ['conn', '_socket']
|
|
259
|
-
.map((key) => client[key])
|
|
260
|
-
.filter((obj) => obj)
|
|
261
|
-
.shift().remoteAddress;
|
|
190
|
+
const ip = client._socket.remoteAddress;
|
|
262
191
|
const key = this.generateKey(context, ip);
|
|
263
192
|
const { totalHits } = await this.storageService.increment(key, ttl);
|
|
264
193
|
|
|
@@ -271,33 +200,149 @@ export class WsThrottlerGuard extends ThrottlerGuard {
|
|
|
271
200
|
}
|
|
272
201
|
```
|
|
273
202
|
|
|
274
|
-
|
|
203
|
+
> info **Hint** If you are using ws, it is necessary to replace the `_socket` with `conn`
|
|
204
|
+
|
|
205
|
+
There's a few things to keep in mind when working with WebSockets:
|
|
275
206
|
|
|
276
|
-
-
|
|
277
|
-
- When a limit is reached, Nest will emit an `exception` event, so make sure there is a listener ready for this
|
|
207
|
+
- Guard cannot be registered with the `APP_GUARD` or `app.useGlobalGuards()`
|
|
208
|
+
- When a limit is reached, Nest will emit an `exception` event, so make sure there is a listener ready for this
|
|
278
209
|
|
|
279
|
-
|
|
210
|
+
> info **Hint** If you are using the `@nestjs/platform-ws` package you can use `client._socket.remoteAddress` instead.
|
|
280
211
|
|
|
281
|
-
|
|
212
|
+
#### GraphQL
|
|
282
213
|
|
|
283
|
-
|
|
284
|
-
the default for Nest, but the [`apollo-server-fastify`](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-fastify) package does not currently support passing `res` to the `context`, meaning headers cannot be properly set.
|
|
285
|
-
- When configuring your `GraphQLModule`, you need to pass an option for `context` in the form
|
|
286
|
-
of `({ req, res}) => ({ req, res })`. This will allow access to the Express Request and Response
|
|
287
|
-
objects, allowing for the reading and writing of headers.
|
|
288
|
-
- You must add in some additional context switching to get the `ExecutionContext` to pass back values correctly (or you can override the method entirely)
|
|
214
|
+
The `ThrottlerGuard` can also be used to work with GraphQL requests. Again, the guard can be extended, but this time the `getRequestResponse` method will be overridden
|
|
289
215
|
|
|
290
|
-
```
|
|
216
|
+
```typescript
|
|
291
217
|
@Injectable()
|
|
292
218
|
export class GqlThrottlerGuard extends ThrottlerGuard {
|
|
293
219
|
getRequestResponse(context: ExecutionContext) {
|
|
294
220
|
const gqlCtx = GqlExecutionContext.create(context);
|
|
295
221
|
const ctx = gqlCtx.getContext();
|
|
296
|
-
return { req: ctx.req, res: ctx.res };
|
|
222
|
+
return { req: ctx.req, res: ctx.res };
|
|
297
223
|
}
|
|
298
224
|
}
|
|
299
225
|
```
|
|
300
226
|
|
|
227
|
+
#### Configuration
|
|
228
|
+
|
|
229
|
+
The following options are valid for the object passed to the array of the `ThrottlerModule`'s options:
|
|
230
|
+
|
|
231
|
+
<table>
|
|
232
|
+
<tr>
|
|
233
|
+
<td><code>name</code></td>
|
|
234
|
+
<td>the name for internal tracking of which throttler set is being used. Defaults to `default` if not passed</td>
|
|
235
|
+
</tr>
|
|
236
|
+
<tr>
|
|
237
|
+
<td><code>ttl</code></td>
|
|
238
|
+
<td>the number of milliseconds that each request will last in storage</td>
|
|
239
|
+
</tr>
|
|
240
|
+
<tr>
|
|
241
|
+
<td><code>limit</code></td>
|
|
242
|
+
<td>the maximum number of requests within the TTL limit</td>
|
|
243
|
+
</tr>
|
|
244
|
+
<tr>
|
|
245
|
+
<td><code>ignoreUserAgents</code></td>
|
|
246
|
+
<td>an array of regular expressions of user-agents to ignore when it comes to throttling requests</td>
|
|
247
|
+
</tr>
|
|
248
|
+
<tr>
|
|
249
|
+
<td><code>skipIf</code></td>
|
|
250
|
+
<td>a function that takes in the <code>ExecutionContext</code> and returns a <code>boolean</code> to short circuit the throttler logic. Like <code>@SkipThrottler()</code>, but based on the request</td>
|
|
251
|
+
</tr>
|
|
252
|
+
</table>
|
|
253
|
+
|
|
254
|
+
If you need to set up storages instead, or want to use a some of the above options in a more global sense, applying to each throttler set, you can pass the options above via the `throttlers` option key and use the below table
|
|
255
|
+
|
|
256
|
+
<table>
|
|
257
|
+
<tr>
|
|
258
|
+
<td><code>storage</code></td>
|
|
259
|
+
<td>a custom storage service for where the throttling should be kept track. <a href="/security/rate-limiting#storages">See here.</a></td>
|
|
260
|
+
</tr>
|
|
261
|
+
<tr>
|
|
262
|
+
<td><code>ignoreUserAgents</code></td>
|
|
263
|
+
<td>an array of regular expressions of user-agents to ignore when it comes to throttling requests</td>
|
|
264
|
+
</tr>
|
|
265
|
+
<tr>
|
|
266
|
+
<td><code>skipIf</code></td>
|
|
267
|
+
<td>a function that takes in the <code>ExecutionContext</code> and returns a <code>boolean</code> to short circuit the throttler logic. Like <code>@SkipThrottler()</code>, but based on the request</td>
|
|
268
|
+
</tr>
|
|
269
|
+
<tr>
|
|
270
|
+
<td><code>throttlers</code></td>
|
|
271
|
+
<td>an array of throttler sets, defined using the table above</td>
|
|
272
|
+
</tr>
|
|
273
|
+
</table>
|
|
274
|
+
|
|
275
|
+
#### Async Configuration
|
|
276
|
+
|
|
277
|
+
You may want to get your rate-limiting configuration asynchronously instead of synchronously. You can use the `forRootAsync()` method, which allows for dependency injection and `async` methods.
|
|
278
|
+
|
|
279
|
+
One approach would be to use a factory function:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
@Module({
|
|
283
|
+
imports: [
|
|
284
|
+
ThrottlerModule.forRootAsync({
|
|
285
|
+
imports: [ConfigModule],
|
|
286
|
+
inject: [ConfigService],
|
|
287
|
+
useFactory: (config: ConfigService) => [
|
|
288
|
+
{
|
|
289
|
+
ttl: config.get('THROTTLE_TTL'),
|
|
290
|
+
limit: config.get('THROTTLE_LIMIT'),
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
}),
|
|
294
|
+
],
|
|
295
|
+
})
|
|
296
|
+
export class AppModule {}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
You can also use the `useClass` syntax:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
@Module({
|
|
303
|
+
imports: [
|
|
304
|
+
ThrottlerModule.forRootAsync({
|
|
305
|
+
imports: [ConfigModule],
|
|
306
|
+
useClass: ThrottlerConfigService,
|
|
307
|
+
}),
|
|
308
|
+
],
|
|
309
|
+
})
|
|
310
|
+
export class AppModule {}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
This is doable, as long as `ThrottlerConfigService` implements the interface `ThrottlerOptionsFactory`.
|
|
314
|
+
|
|
315
|
+
#### Storages
|
|
316
|
+
|
|
317
|
+
The built in storage is an in memory cache that keeps track of the requests made until they have passed the TTL set by the global options. You can drop in your own storage option to the `storage` option of the `ThrottlerModule` so long as the class implements the `ThrottlerStorage` interface.
|
|
318
|
+
|
|
319
|
+
> info **Note** `ThrottlerStorage` can be imported from `@nestjs/throttler`.
|
|
320
|
+
|
|
321
|
+
#### Time Helpers
|
|
322
|
+
|
|
323
|
+
There are a couple of helper methods to make the timings more readable if you prefer to use them over the direct definition. `@nestjs/throttler` exports five different helpers, `seconds`, `minutes`, `hours`, `days`, and `weeks`. To use them, simply call `seconds(5)` or any of the other helpers, and the correct number of milliseconds will be returned.
|
|
324
|
+
|
|
325
|
+
#### Migration Guide
|
|
326
|
+
|
|
327
|
+
For most people, wrapping your options in an array will be enough.
|
|
328
|
+
|
|
329
|
+
If you are using a custom storage, you should wrap you `ttl` and `limit` in an
|
|
330
|
+
array and assign it to the `throttlers` property of the options object.
|
|
331
|
+
|
|
332
|
+
Any `@ThrottleSkip()` should now take in an object with `string: boolean` props.
|
|
333
|
+
The strings are the names of the throttlers. If you do not have a name, pass the
|
|
334
|
+
string `'default'`, as this is what will be used under the hood otherwise.
|
|
335
|
+
|
|
336
|
+
Any `@Throttle()` decorators should also now take in an object with string keys,
|
|
337
|
+
relating to the names of the throttler contexts (again, `'default'` if no name)
|
|
338
|
+
and values of objects that have `limit` and `ttl` keys.
|
|
339
|
+
|
|
340
|
+
> Warning **Important** The `ttl` is now in **milliseconds**. If you want to keep your ttl
|
|
341
|
+
> in seconds for readability, use the `seconds` helper from this package. It just
|
|
342
|
+
> multiplies the ttl by 1000 to make it in milliseconds.
|
|
343
|
+
|
|
344
|
+
For more info, see the [Changelog](https://github.com/nestjs/throttler/blob/master/CHANGELOG.md#500)
|
|
345
|
+
|
|
301
346
|
## Community Storage Providers
|
|
302
347
|
|
|
303
348
|
- [Redis](https://github.com/kkoomen/nestjs-throttler-storage-redis)
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -25,4 +25,5 @@ var throttler_providers_1 = require("./throttler.providers");
|
|
|
25
25
|
Object.defineProperty(exports, "getOptionsToken", { enumerable: true, get: function () { return throttler_providers_1.getOptionsToken; } });
|
|
26
26
|
Object.defineProperty(exports, "getStorageToken", { enumerable: true, get: function () { return throttler_providers_1.getStorageToken; } });
|
|
27
27
|
__exportStar(require("./throttler.service"), exports);
|
|
28
|
+
__exportStar(require("./utilities"), exports);
|
|
28
29
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,uEAAqD;AACrD,gEAA8C;AAC9C,wDAAsC;AACtC,wDAAsC;AACtC,oDAAkC;AAClC,qDAAmC;AACnC,6DAAyE;AAAhE,sHAAA,eAAe,OAAA;AAAE,sHAAA,eAAe,OAAA;AACzC,sDAAoC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,uEAAqD;AACrD,gEAA8C;AAC9C,wDAAsC;AACtC,wDAAsC;AACtC,oDAAkC;AAClC,qDAAmC;AACnC,6DAAyE;AAAhE,sHAAA,eAAe,OAAA;AAAE,sHAAA,eAAe,OAAA;AACzC,sDAAoC;AACpC,8CAA4B"}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { ExecutionContext, ModuleMetadata, Type } from '@nestjs/common/interfaces';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { ThrottlerStorage } from './throttler-storage.interface';
|
|
3
|
+
export type Resolvable<T extends number | string | boolean> = T | ((context: ExecutionContext) => T | Promise<T>);
|
|
4
|
+
export interface ThrottlerOptions {
|
|
5
|
+
name?: string;
|
|
6
|
+
limit: Resolvable<number>;
|
|
7
|
+
ttl: Resolvable<number>;
|
|
5
8
|
ignoreUserAgents?: RegExp[];
|
|
6
|
-
storage?: any;
|
|
7
9
|
skipIf?: (context: ExecutionContext) => boolean;
|
|
8
10
|
}
|
|
11
|
+
export type ThrottlerModuleOptions = Array<ThrottlerOptions> | {
|
|
12
|
+
skipIf?: (context: ExecutionContext) => boolean;
|
|
13
|
+
ignoreUserAgents?: RegExp[];
|
|
14
|
+
storage?: ThrottlerStorage;
|
|
15
|
+
throttlers: Array<ThrottlerOptions>;
|
|
16
|
+
};
|
|
9
17
|
export interface ThrottlerOptionsFactory {
|
|
10
18
|
createThrottlerOptions(): Promise<ThrottlerModuleOptions> | ThrottlerModuleOptions;
|
|
11
19
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { ThrottlerStorageOptions } from './throttler-storage-options.interface';
|
|
2
1
|
import { ThrottlerStorageRecord } from './throttler-storage-record.interface';
|
|
3
2
|
export interface ThrottlerStorage {
|
|
4
|
-
storage: Record<string, ThrottlerStorageOptions>;
|
|
5
3
|
increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;
|
|
6
4
|
}
|
|
7
5
|
export declare const ThrottlerStorage: unique symbol;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttler-storage.interface.js","sourceRoot":"","sources":["../src/throttler-storage.interface.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"throttler-storage.interface.js","sourceRoot":"","sources":["../src/throttler-storage.interface.ts"],"names":[],"mappings":";;;AAUa,QAAA,gBAAgB,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC"}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Resolvable } from './throttler-module-options.interface';
|
|
2
|
+
interface ThrottlerMethodOrControllerOptions {
|
|
3
|
+
limit?: Resolvable<number>;
|
|
4
|
+
ttl?: Resolvable<number>;
|
|
5
|
+
}
|
|
6
|
+
export declare const Throttle: (options: Record<string, ThrottlerMethodOrControllerOptions>) => MethodDecorator & ClassDecorator;
|
|
7
|
+
export declare const SkipThrottle: (skip?: Record<string, boolean>) => MethodDecorator & ClassDecorator;
|
|
3
8
|
export declare const InjectThrottlerOptions: () => PropertyDecorator & ParameterDecorator;
|
|
4
9
|
export declare const InjectThrottlerStorage: () => PropertyDecorator & ParameterDecorator;
|
|
10
|
+
export {};
|
|
@@ -4,29 +4,33 @@ exports.InjectThrottlerStorage = exports.InjectThrottlerOptions = exports.SkipTh
|
|
|
4
4
|
const common_1 = require("@nestjs/common");
|
|
5
5
|
const throttler_constants_1 = require("./throttler.constants");
|
|
6
6
|
const throttler_providers_1 = require("./throttler.providers");
|
|
7
|
-
function setThrottlerMetadata(target,
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
function setThrottlerMetadata(target, options) {
|
|
8
|
+
for (const name in options) {
|
|
9
|
+
Reflect.defineMetadata(throttler_constants_1.THROTTLER_TTL + name, options[name].ttl, target);
|
|
10
|
+
Reflect.defineMetadata(throttler_constants_1.THROTTLER_LIMIT + name, options[name].limit, target);
|
|
11
|
+
}
|
|
10
12
|
}
|
|
11
|
-
const Throttle = (
|
|
13
|
+
const Throttle = (options) => {
|
|
12
14
|
return (target, propertyKey, descriptor) => {
|
|
13
15
|
if (descriptor) {
|
|
14
|
-
setThrottlerMetadata(descriptor.value,
|
|
16
|
+
setThrottlerMetadata(descriptor.value, options);
|
|
15
17
|
return descriptor;
|
|
16
18
|
}
|
|
17
|
-
setThrottlerMetadata(target,
|
|
19
|
+
setThrottlerMetadata(target, options);
|
|
18
20
|
return target;
|
|
19
21
|
};
|
|
20
22
|
};
|
|
21
23
|
exports.Throttle = Throttle;
|
|
22
|
-
const SkipThrottle = (skip = true) => {
|
|
24
|
+
const SkipThrottle = (skip = { default: true }) => {
|
|
23
25
|
return (target, propertyKey, descriptor) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
for (const key in skip) {
|
|
27
|
+
if (descriptor) {
|
|
28
|
+
Reflect.defineMetadata(throttler_constants_1.THROTTLER_SKIP + key, skip[key], descriptor.value);
|
|
29
|
+
return descriptor;
|
|
30
|
+
}
|
|
31
|
+
Reflect.defineMetadata(throttler_constants_1.THROTTLER_SKIP + key, skip[key], target);
|
|
32
|
+
return target;
|
|
27
33
|
}
|
|
28
|
-
Reflect.defineMetadata(throttler_constants_1.THROTTLER_SKIP, skip, target);
|
|
29
|
-
return target;
|
|
30
34
|
};
|
|
31
35
|
};
|
|
32
36
|
exports.SkipThrottle = SkipThrottle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttler.decorator.js","sourceRoot":"","sources":["../src/throttler.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AACxC,+DAAuF;AACvF,+DAAyE;
|
|
1
|
+
{"version":3,"file":"throttler.decorator.js","sourceRoot":"","sources":["../src/throttler.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AACxC,+DAAuF;AACvF,+DAAyE;AAQzE,SAAS,oBAAoB,CAC3B,MAAW,EACX,OAA2D;IAE3D,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE;QAC1B,OAAO,CAAC,cAAc,CAAC,mCAAa,GAAG,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACxE,OAAO,CAAC,cAAc,CAAC,qCAAe,GAAG,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;KAC7E;AACH,CAAC;AASM,MAAM,QAAQ,GAAG,CACtB,OAA2D,EACzB,EAAE;IACpC,OAAO,CACL,MAAW,EACX,WAA6B,EAC7B,UAAyC,EACzC,EAAE;QACF,IAAI,UAAU,EAAE;YACd,oBAAoB,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAChD,OAAO,UAAU,CAAC;SACnB;QACD,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC,CAAC;AAfW,QAAA,QAAQ,YAenB;AASK,MAAM,YAAY,GAAG,CAC1B,OAAgC,EAAE,OAAO,EAAE,IAAI,EAAE,EACf,EAAE;IACpC,OAAO,CACL,MAAW,EACX,WAA6B,EAC7B,UAAyC,EACzC,EAAE;QACF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;YACtB,IAAI,UAAU,EAAE;gBACd,OAAO,CAAC,cAAc,CAAC,oCAAc,GAAG,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;gBAC1E,OAAO,UAAU,CAAC;aACnB;YACD,OAAO,CAAC,cAAc,CAAC,oCAAc,GAAG,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;YAChE,OAAO,MAAM,CAAC;SACf;IACH,CAAC,CAAC;AACJ,CAAC,CAAC;AAjBW,QAAA,YAAY,gBAiBvB;AAOK,MAAM,sBAAsB,GAAG,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,qCAAe,GAAE,CAAC,CAAC;AAAzD,QAAA,sBAAsB,0BAAmC;AAO/D,MAAM,sBAAsB,GAAG,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,qCAAe,GAAE,CAAC,CAAC;AAAzD,QAAA,sBAAsB,0BAAmC"}
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
2
|
import { Reflector } from '@nestjs/core';
|
|
3
|
-
import { ThrottlerModuleOptions } from './throttler-module-options.interface';
|
|
3
|
+
import { ThrottlerModuleOptions, ThrottlerOptions } from './throttler-module-options.interface';
|
|
4
4
|
import { ThrottlerStorage } from './throttler-storage.interface';
|
|
5
|
+
import { ThrottlerLimitDetail } from './throttler.guard.interface';
|
|
5
6
|
export declare class ThrottlerGuard implements CanActivate {
|
|
6
7
|
protected readonly options: ThrottlerModuleOptions;
|
|
7
8
|
protected readonly storageService: ThrottlerStorage;
|
|
8
9
|
protected readonly reflector: Reflector;
|
|
9
10
|
protected headerPrefix: string;
|
|
10
11
|
protected errorMessage: string;
|
|
12
|
+
protected throttlers: Array<ThrottlerOptions>;
|
|
13
|
+
protected commonOptions: Pick<ThrottlerOptions, 'skipIf' | 'ignoreUserAgents'>;
|
|
11
14
|
constructor(options: ThrottlerModuleOptions, storageService: ThrottlerStorage, reflector: Reflector);
|
|
15
|
+
onModuleInit(): Promise<void>;
|
|
12
16
|
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
13
|
-
protected
|
|
14
|
-
protected
|
|
17
|
+
protected shouldSkip(_context: ExecutionContext): Promise<boolean>;
|
|
18
|
+
protected handleRequest(context: ExecutionContext, limit: number, ttl: number, throttler: ThrottlerOptions): Promise<boolean>;
|
|
19
|
+
protected getTracker(req: Record<string, any>): Promise<string>;
|
|
15
20
|
protected getRequestResponse(context: ExecutionContext): {
|
|
16
21
|
req: Record<string, any>;
|
|
17
22
|
res: Record<string, any>;
|
|
18
23
|
};
|
|
19
|
-
protected generateKey(context: ExecutionContext, suffix: string): string;
|
|
20
|
-
protected throwThrottlingException(context: ExecutionContext): void
|
|
24
|
+
protected generateKey(context: ExecutionContext, suffix: string, name: string): string;
|
|
25
|
+
protected throwThrottlingException(context: ExecutionContext, throttlerLimitDetail: ThrottlerLimitDetail): Promise<void>;
|
|
26
|
+
protected getErrorMessage(_context: ExecutionContext, _throttlerLimitDetail: ThrottlerLimitDetail): Promise<string>;
|
|
27
|
+
private resolveValue;
|
|
21
28
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttler.guard.interface.js","sourceRoot":"","sources":["../src/throttler.guard.interface.ts"],"names":[],"mappings":""}
|