@envelop/rate-limiter 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/index.d.ts +8 -0
- package/index.js +25 -3
- package/index.mjs +25 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,6 +45,18 @@ type Query {
|
|
|
45
45
|
|
|
46
46
|
> You can apply that directive to any GraphQL `field` definition, not only to root fields.
|
|
47
47
|
|
|
48
|
+
### Error message interpolation
|
|
49
|
+
|
|
50
|
+
The `message` argument of the `@rateLimit` directive can be dynamic. You `{{var}}` or `{{ var }}` syntax to interpolate variables.
|
|
51
|
+
|
|
52
|
+
```graphql
|
|
53
|
+
type Query {
|
|
54
|
+
posts: [Post]! @rateLimit(window: "5s", max: 10, message: "Too many calls made by {{ id }}")
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
> The only available variable so far is `id`.
|
|
59
|
+
|
|
48
60
|
## Notes
|
|
49
61
|
|
|
50
62
|
You can find more details here: hhttps://github.com/teamplanes/graphql-rate-limit#readme
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Plugin } from '@envelop/core';
|
|
2
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
2
3
|
import { getGraphQLRateLimiter } from 'graphql-rate-limit';
|
|
3
4
|
export * from './utils';
|
|
4
5
|
export declare class UnauthenticatedError extends Error {
|
|
@@ -8,6 +9,13 @@ export declare const DIRECTIVE_SDL = "\n directive @rateLimit(max: Int, window:
|
|
|
8
9
|
export declare type RateLimiterPluginOptions = {
|
|
9
10
|
identifyFn: IdentifyFn;
|
|
10
11
|
rateLimitDirectiveName?: 'rateLimit' | string;
|
|
12
|
+
transformError?: (message: string) => Error;
|
|
13
|
+
onRateLimitError?: (event: {
|
|
14
|
+
error: string;
|
|
15
|
+
identifier: string;
|
|
16
|
+
context: unknown;
|
|
17
|
+
info: GraphQLResolveInfo;
|
|
18
|
+
}) => void;
|
|
11
19
|
};
|
|
12
20
|
export declare const useRateLimiter: (options: RateLimiterPluginOptions) => Plugin<{
|
|
13
21
|
rateLimiterFn: ReturnType<typeof getGraphQLRateLimiter>;
|
package/index.js
CHANGED
|
@@ -22,7 +22,7 @@ const DIRECTIVE_SDL = /* GraphQL */ `
|
|
|
22
22
|
const useRateLimiter = (options) => {
|
|
23
23
|
const rateLimiterFn = graphqlRateLimit.getGraphQLRateLimiter({ identifyContext: options.identifyFn });
|
|
24
24
|
return {
|
|
25
|
-
async onContextBuilding({
|
|
25
|
+
async onContextBuilding({ extendContext }) {
|
|
26
26
|
extendContext({
|
|
27
27
|
rateLimiterFn,
|
|
28
28
|
});
|
|
@@ -39,15 +39,37 @@ const useRateLimiter = (options) => {
|
|
|
39
39
|
const message = messageNode.value;
|
|
40
40
|
const max = parseInt(maxNode.value);
|
|
41
41
|
const window = windowNode.value;
|
|
42
|
-
const
|
|
43
|
-
|
|
42
|
+
const id = options.identifyFn(context);
|
|
43
|
+
const errorMessage = await context.rateLimiterFn({ parent: root, args, context, info }, {
|
|
44
|
+
max,
|
|
45
|
+
window,
|
|
46
|
+
message: interpolate(message, {
|
|
47
|
+
id,
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
if (errorMessage) {
|
|
51
|
+
if (options.onRateLimitError) {
|
|
52
|
+
options.onRateLimitError({
|
|
53
|
+
error: errorMessage,
|
|
54
|
+
identifier: id,
|
|
55
|
+
context,
|
|
56
|
+
info,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (options.transformError) {
|
|
60
|
+
throw options.transformError(errorMessage);
|
|
61
|
+
}
|
|
44
62
|
throw new Error(errorMessage);
|
|
63
|
+
}
|
|
45
64
|
}
|
|
46
65
|
},
|
|
47
66
|
};
|
|
48
67
|
},
|
|
49
68
|
};
|
|
50
69
|
};
|
|
70
|
+
function interpolate(message, args) {
|
|
71
|
+
return message.replace(/\{{([^)]*)\}}/g, (_, key) => args[key.trim()]);
|
|
72
|
+
}
|
|
51
73
|
|
|
52
74
|
exports.DIRECTIVE_SDL = DIRECTIVE_SDL;
|
|
53
75
|
exports.UnauthenticatedError = UnauthenticatedError;
|
package/index.mjs
CHANGED
|
@@ -18,7 +18,7 @@ const DIRECTIVE_SDL = /* GraphQL */ `
|
|
|
18
18
|
const useRateLimiter = (options) => {
|
|
19
19
|
const rateLimiterFn = getGraphQLRateLimiter({ identifyContext: options.identifyFn });
|
|
20
20
|
return {
|
|
21
|
-
async onContextBuilding({
|
|
21
|
+
async onContextBuilding({ extendContext }) {
|
|
22
22
|
extendContext({
|
|
23
23
|
rateLimiterFn,
|
|
24
24
|
});
|
|
@@ -35,14 +35,36 @@ const useRateLimiter = (options) => {
|
|
|
35
35
|
const message = messageNode.value;
|
|
36
36
|
const max = parseInt(maxNode.value);
|
|
37
37
|
const window = windowNode.value;
|
|
38
|
-
const
|
|
39
|
-
|
|
38
|
+
const id = options.identifyFn(context);
|
|
39
|
+
const errorMessage = await context.rateLimiterFn({ parent: root, args, context, info }, {
|
|
40
|
+
max,
|
|
41
|
+
window,
|
|
42
|
+
message: interpolate(message, {
|
|
43
|
+
id,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
if (errorMessage) {
|
|
47
|
+
if (options.onRateLimitError) {
|
|
48
|
+
options.onRateLimitError({
|
|
49
|
+
error: errorMessage,
|
|
50
|
+
identifier: id,
|
|
51
|
+
context,
|
|
52
|
+
info,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (options.transformError) {
|
|
56
|
+
throw options.transformError(errorMessage);
|
|
57
|
+
}
|
|
40
58
|
throw new Error(errorMessage);
|
|
59
|
+
}
|
|
41
60
|
}
|
|
42
61
|
},
|
|
43
62
|
};
|
|
44
63
|
},
|
|
45
64
|
};
|
|
46
65
|
};
|
|
66
|
+
function interpolate(message, args) {
|
|
67
|
+
return message.replace(/\{{([^)]*)\}}/g, (_, key) => args[key.trim()]);
|
|
68
|
+
}
|
|
47
69
|
|
|
48
70
|
export { DIRECTIVE_SDL, UnauthenticatedError, getDirective, useRateLimiter };
|