-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Directive proposal for opting out of null bubbling #1050
base: semantic-non-null
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for graphql-spec-draft ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
in terms of naming - we have a proposed PR on graphql-java graphql-java/graphql-java#3772
This is not merged (yet) but its a name suggestion I would like to link here |
Another option: enum __ErrorBehavior {
"""
Non-nullable positions that error cause the error to propagate to the nearest nullable
ancestor position. The error is added to the "errors" list.
"""
PROPAGATE
"""
Positions that error are replaced with a `null` and an error is added to the "errors"
list.
"""
NULL
"""
If any error occurs, abort the entire request and just return the error in the "errors"
list. (No partial success.)
"""
ABORT
"""
Positions that error are replaced with an inline representation of the error, and
the path to the error is added to the "errorPaths" list.
"""
INLINE
}
directive @behavior(onError: __ErrorBehavior! = PROPAGATE) on QUERY | MUTATION | SUBSCRIPTION (To be clear: I'm not proposing to implement Rather than going error-centric for this, I felt like a single directive to handle many behaviors might make sense. We could later expand this so that you can do things like |
Agree 100% with one nitpick: I would merge
enum __ErrorBehavior {
"""
Positions that error are replaced with a `null` and an error is added to the "errors"
list. Non-JSON encodings may decide to represent errors inline.
"""
LOCAL
"""
Non-nullable positions that error cause the error to propagate to the nearest nullable
ancestor position. The error is added to the "errors" list.
"""
PROPAGATE
"""
If any error occurs, abort the entire request and just return the error in the "errors"
list. (No partial success.)
"""
ABORT
} |
I think with @benjie's version, inlining errors could work even with JSON encodings. It's often said that we cannot embed errors within the response because a custom scalar could be anything, but we can use an In theory, whether errors are inlined is orthogonal to whether errors propagate or cause execution to abort, and could be a separate argument. From that perspective, propagation vs abortion vs neither could be an A counter-counter-argument could be that we have assumed that the only reason to propagate errors is for null-protection, i.e. type safety reasons on the client. I think that is the general assumption. But there is another potential advantage. Propagation allows us to skip executing sibling fields that are presumably unnecessary in the context of a catastrophic I might still argue to separate them, kind of makes things more descriptive. |
@yaacovCR Right. Apologies I misread the description of All in all, I would probably challenge the naming there but this is probably a question for later, if we decide to implement it. The good thing about the enum solution is that we can start with just |
So I thought about this more, and as Yaacov says, enum __ErrorBoundary {
"""Errors should propagate to the root of the operation (effectively aborting the entire request)"""
ROOT
"""Errors should propagate to the first nullable position"""
NULLABLE
"""Errors should not propagate"""
LOCAL
}
enum __ErrorLocation {
"""Errored positions should become `null` and the error should be collected in a separate "errors" list."""
ERRORS_LIST
"""Errors should be serialized inline, and a separate "errorPaths" list should track where they occurred."""
INLINE
}
directive @behavior(
errorBoundary: __ErrorBoundary = NULLABLE
errorLocation: __ErrorLocation = ERRORS_LIST
) on OPERATION |
What is the advantage of using |
It's more legible when reading the response data as a human/developer. Rather than seeing |
(The errorPaths would help a GraphQL client to know a) those positions are definitely errors, not custom scalars, and b) to know where the errors occurred without having to walk the response tree.) |
Gotcha, thanks! 👍
Wondering if we could add a mode where schema authors could say "my custom scalar will never contain enum __ErrorLocation {
"""Errored positions should become `null` and the error should be collected in a separate "errors" list."""
ERRORS_LIST
"""Errors should be serialized inline, and a separate "errorPaths" list should track where they occurred."""
MOSTLY_INLINE
"""Errors should be serialized inline, custom scalars must not be JSON objects containing a `__$$error` key."""
INLINE
} I would really like to see response streaming supported one day to help reduce latency and inlining errors is required for that. |
In my experience schemas that accept JSON do minimal validation on it; walking the entire tree to ensure it doesn't contain (Also, for things like graphql-toe, it's more performant to be informed where the errors occur and handle just those positions rather than having to walk the entire response tree to look for them before allowing the client to render.) |
My whole grudge with And making It can be argued that with parallel execution it's already the case that the server needs to buffer a little bit but it could stil be optimized for mutations and for the queries where the slow field happens later in the query so this is something that'd be cool to have regardless. |
~This pull request adds support for `@onError(action: NULL)` to disable error propagation for error aware clients:~ This pull request adds support for `@experimental_disableErrorPropagation` to disable error propagation for error aware clients: ```graphql """ Disables error propagation. """ directive @experimental_disableErrorPropagation on QUERY | MUTATION | SUBSCRIPTION ``` I'm not super used to write TypeScript, feel free to amend the PR as needed but I figured it'd be good to have. The logic is unconditional. The matching [graphql-java](graphql-java/graphql-java#3772) PR has a specific opt-in flag so that it's not enabled by accident in the very unlikely event that a schema already contains a matching directive. Let me know if this is an issue. Many thanks @JoviDeCroock for pointing me in the right direction 🙏 See graphql/nullability-wg#85 See graphql/graphql-spec#1050 --------- Co-authored-by: Jovi De Croock <decroockjovi@gmail.com>
Counterproposal: this should be a request option not a directive: graphql/nullability-wg#86 |
eed6336
to
27ab6a1
Compare
This PR builds on #1065.
This introduces a directive on operations that disables the null/error propagation behavior by treating all Non-Null types as if they were Semantic-Non-Null types (see #1065).
The specific name of this directive (currently
@disableNullPropagation
) is open to workshopping:@noBubblesPlz
😉 or@tepid
🤣@disableNullPropagation
/@disableErrorPropagation
/@noNullPropagation
/@noPropagation
/@nullOnError
/ etc@localErrors
@dontHandleErrorsForMeIKnowWhatImDoing
Implemented in graphql/graphql-js#4192 as part of semantic non-null support.