envelop
envelop copied to clipboard
feat: `perform` function that does parsing, validation, context assembly and execution/subscription
Closes #1493
Necessary for #1490
perform
function usage
const { perform } = getEnveloped({ initial: 'ctx' });
const { operationName, query, variables } = JSON.parse(payload);
// Will never throw GraphQL errors
const result = await perform({ operationName, query, variables }, { extension: 'ctx' });
// Result will contain errors for parsing/validation issues or the execution/subscription result(s)
return JSON.stringify(result);
onPerform
and onPerformDone
plugin usage
import { Plugin } from '@envelop/core';
function usePerform(): Plugin {
return {
onPerform({ context, extendContext, params, setParams, setResult }) {
// before performing
// context: assembled context
// extendContext: extend assembled context
// params: GraphQL parameters passed to perform
// setParams: replace GraphQL parameters before performing
// setResult: sets an early result for immediate response
return {
onPerformDone({ result, setResult }) {
// after performing
// result: perform result, contains errors or result(s)
// setResult: replace the result
},
};
},
};
}
TODO
- [x] Documentation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
Name | Status | Preview | Updated |
---|---|---|---|
envelop | ✅ Ready (Inspect) | Visit Preview | Sep 15, 2022 at 1:35PM (UTC) |
🦋 Changeset detected
Latest commit: 01669df43b17b650668254a2035d7f0890489b02
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 35 packages
Name | Type |
---|---|
@envelop/core | Minor |
@envelop/testing | Major |
@envelop/apollo-datasources | Major |
@envelop/apollo-federation | Major |
@envelop/apollo-server-errors | Major |
@envelop/apollo-tracing | Major |
@envelop/auth0 | Major |
@envelop/dataloader | Major |
@envelop/depth-limit | Major |
@envelop/disable-introspection | Major |
@envelop/execute-subscription-event | Major |
@envelop/extended-validation | Major |
@envelop/filter-operation-type | Major |
@envelop/fragment-arguments | Major |
@envelop/generic-auth | Major |
@envelop/graphql-jit | Major |
@envelop/graphql-middleware | Major |
@envelop/graphql-modules | Major |
@envelop/immediate-introspection | Major |
@envelop/live-query | Major |
@envelop/newrelic | Major |
@envelop/on-resolve | Major |
@envelop/opentelemetry | Major |
@envelop/operation-field-permissions | Major |
@envelop/parser-cache | Major |
@envelop/persisted-operations | Major |
@envelop/preload-assets | Major |
@envelop/prometheus | Major |
@envelop/rate-limiter | Major |
@envelop/resource-limitations | Major |
@envelop/response-cache | Major |
@envelop/sentry | Major |
@envelop/statsd | Major |
@envelop/validation-cache | Major |
@envelop/response-cache-redis | Patch |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
diff --git a/website/algolia-lockfile.json b/website/algolia-lockfile.json
index d5281857..61eb96b1 100644
--- a/website/algolia-lockfile.json
+++ b/website/algolia-lockfile.json
@@ -361,6 +361,11 @@
"children": [],
"title": "`onSchemaChange(api)`",
"anchor": "onschemachangeapi"
+ },
+ {
+ "children": [],
+ "title": "`onPerform(api)`",
+ "anchor": "onperformapi"
}
],
"title": "`onPluginInit(api)`",
🚀 Snapshot Release (alpha
)
The latest changes of this PR are available as alpha
on npm (based on the declared changesets
):
Package | Version | Info |
---|---|---|
@envelop/core |
3.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/apollo-datasources |
2.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/apollo-federation |
3.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/apollo-server-errors |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/apollo-tracing |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/auth0 |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/dataloader |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/depth-limit |
2.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/disable-introspection |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/execute-subscription-event |
3.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/extended-validation |
2.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/filter-operation-type |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/fragment-arguments |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/generic-auth |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/graphql-jit |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/graphql-middleware |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/graphql-modules |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/immediate-introspection |
1.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/live-query |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/newrelic |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/on-resolve |
2.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/opentelemetry |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/operation-field-permissions |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/parser-cache |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/persisted-operations |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/preload-assets |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/prometheus |
7.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/rate-limiter |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/resource-limitations |
3.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/response-cache |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/response-cache-redis |
2.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/sentry |
4.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/statsd |
3.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/validation-cache |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/testing |
5.0.0-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
@envelop/types |
2.4.1-alpha-20220909172432-a082c09b |
npm ↗︎ unpkg ↗︎ |
❌ Benchmark Failed
Failed assertions detected: some GraphQL operations included in the loadtest are failing.
If the performance regression is expected, please increase the failing threshold.
✓ no_errors
✗ expected_result
↳ 0% — ✓ 0 / ✗ 304912
checks.............................................: 50.00% ✓ 304912 ✗ 304912
data_received......................................: 85 MB 564 kB/s
data_sent..........................................: 132 MB 880 kB/s
envelop_total......................................: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
✓ { mode:envelop-cache-jit }.......................: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
✓ { mode:envelop-just-cache }......................: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
✓ { mode:graphql-js }..............................: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
✓ { mode:prom-tracing }............................: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_blocked...................................: avg=2.6µs min=600ns med=1.7µs max=12.01ms p(90)=2.2µs p(95)=2.8µs
http_req_connecting................................: avg=243ns min=0s med=0s max=11.05ms p(90)=0s p(95)=0s
http_req_duration..................................: avg=3.85ms min=139.4µs med=4.06ms max=164.41ms p(90)=5.99ms p(95)=7.25ms
✓ { mode:envelop-cache-and-no-internal-tracing }...: avg=3.48ms min=142.3µs med=3.59ms max=49.93ms p(90)=5.09ms p(95)=6.33ms
✓ { mode:envelop-cache-jit }.......................: avg=3.54ms min=154.2µs med=3.79ms max=34.91ms p(90)=5.21ms p(95)=6.51ms
✓ { mode:envelop-just-cache }......................: avg=3.62ms min=147.3µs med=3.78ms max=164.41ms p(90)=5.31ms p(95)=6.79ms
✓ { mode:graphql-js }..............................: avg=5.56ms min=256.21µs med=5.67ms max=44.5ms p(90)=7.57ms p(95)=9.11ms
✓ { mode:prom-tracing }............................: avg=3.46ms min=139.4µs med=3.54ms max=32.98ms p(90)=5.24ms p(95)=6.6ms
http_req_failed....................................: 100.00% ✓ 304912 ✗ 0
http_req_receiving.................................: avg=39.15µs min=7.1µs med=27.4µs max=17.81ms p(90)=34.1µs p(95)=40.8µs
http_req_sending...................................: avg=23.05µs min=4.4µs med=10.4µs max=19.7ms p(90)=14.3µs p(95)=19.9µs
http_req_tls_handshaking...........................: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...................................: avg=3.79ms min=108.7µs med=4ms max=164.38ms p(90)=5.93ms p(95)=7.13ms
http_reqs..........................................: 304912 2032.6089/s
iteration_duration.................................: avg=4.94ms min=232.41µs med=4.33ms max=988.66ms p(90)=6.34ms p(95)=7.98ms
iterations.........................................: 304912 2032.6089/s
vus................................................: 10 min=10 max=18
vus_max............................................: 20 min=20 max=20
How do I use the
onPerformDone
with Apollo Server?
I've changed the Apollo Server example with https://github.com/n1ru4l/envelop/pull/1515/commits/827fb179a91e8ec89b7c8460c09bc650eb112b72.
The examples need to be updated.
Not all examples support the perform
function. Most of them require the isolated GraphQL functions.
I updated those that have support, simple-http
and apollo-server
.
Should perform be used within the testkit/replace the testkit?
I thought about this too, but didn't want to overdo this PR - thought about making a separate one. But, if you want, I can pack it into here.
I thought about this too, but didn't want to overdo this PR - thought about making a separate one. But, if you want, I can pack it here.
I think this should be in here, as onPerformDone
seems to now be mandatory for some plugins, otherwise, we need to mark what phases each plugin hooks into explicitly in order to let people know what to expect.
I think teaching people that they have to call this (additional?) function with the result is the easiest way.
I added the perform
function to the testkit and refactored all compatible tests to use it. Things to note:
- Replaced all usages of
execute
withperform
-
mockPhase
cannot be used withperform
; however, no tests use it - does the testkit even need it? Other users? - Since perform catches errors and puts them inside the result, the Auth0 plugin test changed a bit: see here.
There is still no way for applying the onPerformDone logic when not using the perform function. Which makes it impossible for users that do not use perform to leverage plugins that use the onPerformDone hook 🤔 .
@n1ru4l that is the thing, if you don't use perform
you cannot use onPerform
(onPerformDone
neither because it's returned from onPerform
). Exactly because of this, I put onPerformDone
in onPerform
instead of it being a top level hook.
Perform is the only function in envelop that wraps parsing and validation errors - you must use it to leverage plugins using these hooks. The problem is described in #1493.
Do you have some alternatives maybe?
If I understand well what n1ru4l says, it's not a matter of using or not perform
.
It's more about plugin developers not being able to use this hook since it can't know in advance if there users will use perform
or the original functions. This makes the hook difficult to use and probably not very usefull for most plugins that aims to be reused and distributed as a package.
It's more about plugin developers not being able to use this hook since it can't know in advance if there users will use perform or the original functions. This makes the hook difficult to use and probably not very usefull for most plugins that aims to be reused and distributed as a package.
Yes, I understand this too. But I don't see a lot of feasible solutions, maybe one of them would be: log a warning (throw error? too harsh?) if a plugin uses the perform hooks but the invoker didn't use perform. 🤔
What if we augment each gql function so that it calls onPerformDone
so that plugins still get some result manipulation capabilities?
-
parse
- catch throws, format as exec result and run through hooks. If the invoker is parse from getEnvloped, take first error from the exec result and bubble it instead -
validate
- caught or returned errors are formatted as exec result and ran through hooks. If invoker is from getEnveloped, return just the errors from the exec result
This way, users can notice when their results are incomplete with respectful plugins and read about how to fix it - the plugins should state this.
However, if the plugin doesn't expect changing full exec results for parsing and validating - nobody will notice.
Still there is this question: how to invoke onPerform
? Or alternatively inline onPerformDone
?