At a glance
- Identifier: #1165
- Stage: RFC 0 / Strawman
- Champion: @benjie
- Latest activity: Spec PR created on 2025-04-30
- Spec PR: https://github.com/graphql/graphql-spec/pull/1165
- Related:
Spec PR description
This is essentially solution 8 to the Semantic Nullability RFC:
- Enables semantic nullability to be reflected in schemas without breaking legacy behavior.
- Facilitates incremental adoption of modern error handling without requiring disruptive changes.
- Requires minimal spec impact and is fully optional for implementations.
- Reflects transitional nature of this change in behavior
I've based it on:
- #1163
since, like all solutions to the semantic nullability problem1, it is designed to enable clients with error propagation disabled to leverage the true nullability of the underlying data without breaking legacy clients. The approach could be rebuilt atop an alternative method of toggling error propagation, for example a directive-based approach.
This PR introduces an appendix to the GraphQL specification defining an optional solution to the semantic nullability problem using the following key mechanisms:
@noPropagate2 directive — allows schema authors to annotateNon-Nullreturn types as transitional, suppressing propagation but preserving runtime error generation.- Transitional Non-Null semantics — errors at these positions behave like nullable fields in terms of (no!) propagation but like non-nullable fields in value completion (error on null).
- New
__Field.noPropagateLevels: [Int!]field — exposes transitional status to modern clients. - Transitional non-null hidden from legacy clients — tooling using the legacy
PROPAGATEerror behavior will get results from__Field.typethat unwrap transitional non-null types.3
This solution attempts to address all of the feedback on previous solutions to this problem, whilst being explicitly transitional. It:
- Is optional: explicitly only for schemas supporting legacy clients
- Requires no changes to the main spec text
- Introduces no new syntax
- True to its name: an error here will not propagate (
@noPropagate), regardless of whether error propagation is enabled or disabled. - Maintains introspection results for existing (deployed) clients and tooling
- Maintains error boundaries for existing (deployed) clients
- Allows all new schemas and new fields to use
!(non-null) directly for semantically non-null positions - Allows existing fields to use
!(non-null) for error handling clients without breaking legacy clients by adding the@noPropagatedirective4 - Can be adopted gradually, field-by-field, or en masse by applying
@noPropagateto all nullable positions. - Can be removed from each field the moment no legacy clients query it
Timeline
- Spec PR created on 2025-04-30 by benjie
- 5 commits pushed on 2025-04-30:
- benjie committed "Add Transitional Non-Null to the appendix"
- benjie committed "Tweaks"
- benjie committed "Revise first paragraph"
- benjie committed "Overhaul overview"
- benjie committed "Tweak titles"
Footnotes
-
Except solution 5 ↩
-
This is essentially the same as the
@semanticNonNulldirective, but more strictly defined and reflected through introspection. ↩ -
This may be controversial, but I truly think it's the right decision. All new tooling (and all new clients!) should use
onError: ABORToronError: NO_PROPAGATE, and thus will see the true introspection. Existing tooling doesn't know aboutonErrorand so should not see these "transitional" non-null types. ↩ -
And if you forget to add it, adding it later is only a potentially breaking change for any new versions of legacy clients deployed since the change; error-handling clients (
NO_PROPAGATEorABORT) are unimpacted. ↩