-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add Transitional Non-Null appendix (@noPropagate
directive)
#1165
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
Open
benjie
wants to merge
5
commits into
error-behavior2
Choose a base branch
from
no-propagate
base: error-behavior2
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# C. Appendix: Transitional Non-Null | ||
|
||
Note: This appendix defines an optional mechanism enabling existing fields to be | ||
marked as `Non-Null` for clients that opt out of error propagation without | ||
changing the error propagation boundaries for deployed legacy clients. | ||
Implementations are not required to support this feature, but doing so enables | ||
gradual migration toward semantic nullability while preserving compatibility. | ||
|
||
## Overview | ||
|
||
With the introduction of _error behavior_, clients can take responsibility for | ||
handling of _execution error_: correlating {"errors"} in the result with `null` | ||
values inside {"data"} and thereby removing the ambiguity that error propagation | ||
originally set out to solve. If all clients adopt this approach then schema | ||
designers can, and should, reflect true nullability in the schema, marking | ||
fields as `Non-Null` based on their data semantics without regard to whether or | ||
not they might error. | ||
|
||
However, legacy clients may not perform this correlation. Introducing `Non-Null` | ||
in such cases could cause errors to propagate further, potentially turning a | ||
previously handled error in a single field into a full-screen error in the | ||
application. | ||
|
||
To support a smooth transition, this appendix introduces the `@noPropagate` | ||
directive and the concept of _transitional_ Non-Null types. These wrappers raise | ||
errors like regular `Non-Null` types, but suppress propagation and appear | ||
nullable in introspection when using the legacy {"PROPAGATE"} _error behavior_. | ||
|
||
## The @noPropagate Directive | ||
|
||
```graphql | ||
directive @noPropagate(levels: [Int!]! = [0]) on FIELD_DEFINITION | ||
``` | ||
|
||
The `@noPropagate` directive instructs the system to mark the non-null types at | ||
the given levels in the field's return type as "transitional" non-null types | ||
(see [Transitional Non-Null Type](#sec-Transitional-Non-Null-Type)). | ||
|
||
The `levels` argument identifies levels within the return type by counting each | ||
list wrapper. Level 0 refers to the base type; each nested list increases the | ||
level by 1 for its inner type. For the avoidance of doubt: `Non-Null` wrappers | ||
do not increase the count. | ||
|
||
If a listed level corresponds to a nullable type in the return type, it has no | ||
effect. | ||
|
||
For a field that does not return a list type you do not need to specify levels. | ||
If a field returns a list type and you wish to mark the inner type as | ||
`@noPropagate` only then you would provide `@noPropagate(levels: [1])`. | ||
|
||
This example outlines how you might introduce semantic nullability into existing | ||
fields in your schema, to reduce the number of null checks your error-handling | ||
clients need to perform. Remember: new fields should reflect the semantic | ||
nullability immediately, they do not need the `@noPropagate` directive since | ||
there is no legacy to support. | ||
|
||
```diff example | ||
type Query { | ||
- myString: String | ||
+ myString: String! @noPropagate | ||
- myString2: String | ||
+ myString2: String! @noPropagate(levels: [0]) | ||
- myList: [Int]! | ||
+ myList: [Int!]! @noPropagate(levels: [1]) | ||
} | ||
``` | ||
|
||
```graphql example | ||
type Query { | ||
myString: String! @noPropagate | ||
# Equivalent to the above | ||
myString2: String! @noPropagate(levels: [0]) | ||
myList: [Int!]! @noPropagate(levels: [1]) | ||
} | ||
``` | ||
|
||
## Transitional Non-Null | ||
|
||
A "transitional" Non-Null type is a variant of a [Non-Null](#sec-Non-Null) type | ||
that behaves identically to Non-Null with two exceptions: | ||
|
||
1. If an _execution error_ occurs in this response position, the error does not | ||
propagate to the parent _response position_, instead the response position is | ||
set to {null}. | ||
2. When the _error behavior_ of the request is {"PROPAGATE"}, this _response | ||
position_ must be exposed as nullable in introspection. | ||
|
||
### Changes to Handling Execution Errors | ||
|
||
When interpreting the | ||
[Handling Execution Errors](#sec-Handling-Execution-Errors) and | ||
[Errors and Non-Null Types](#sec-Executing-Selection-Sets.Errors-and-Non-Null-Types) | ||
sections of the specification, Transitional Non-Null types should be treated as | ||
if they were nullable types. This does not apply to {CompleteValue()} which | ||
should still raise an _execution error_ if {null} is returned for a Transitional | ||
Non-Null type. | ||
|
||
### Changes to Introspection | ||
|
||
Note: Transitional Non-Null types do not appear in the type system as a distinct | ||
\_\_TypeKind. They are unwrapped to nullable types in introspection when the | ||
error behavior is {"PROPAGATE"}, and appear as {"NON_NULL"} otherwise. | ||
|
||
**\_\_Field.type** | ||
|
||
When the request _error behavior_ is {"PROPAGATE"}, the `type` field on the | ||
`__Field` introspection type must return a `__Type` that represents the type of | ||
value returned by this field with the transitional Non-Null wrapper types | ||
unwrapped at every level. | ||
|
||
**\_\_Field.noPropagateLevels** | ||
|
||
This additional field should be added to introspection: | ||
|
||
```graphql | ||
extend type __Field { | ||
noPropagateLevels: [Int!] | ||
} | ||
``` | ||
|
||
The list must match the `levels` that would be passed to `@noPropagate` to | ||
describe the field’s transitional Non-Null wrappers, or `null` if no | ||
`@noPropagate` would be needed. It must not be an empty list. | ||
|
||
### Changes to the Type System | ||
|
||
When representing a GraphQL schema using the type system definition language, | ||
any field whose return type involves Transitional Non-Null types must indicate | ||
this via the `@noPropagate` directive. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some alternative names:
@semantic
- This field type is semantically non-null. It will never have a null value except in case of error, but we since errors happen it may have a null value@soft
- This is a "soft" non-null in that it is not enforced by propagationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@transitionalErrorBoundary
@transitionallyNullable
@localErrors
??