Add Transitional Non-Null appendix (`@noPropagate` directive)
At a glance​
- Identifier: #1165
- Stage: RFC0: Strawman
- Champion: @benjie
- PR: Add Transitional Non-Null appendix (`@noPropagate` directive)
- Related:
- SemanticNullability (Semantic Nullability)
Timeline​
- Spec PR created on 2025-04-30 by benjie
- 5 commits pushed on 2025-04-30:
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:
@noPropagate
2 directive — allows schema authors to annotateNon-Null
return 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
PROPAGATE
error behavior will get results from__Field.type
that unwrap transitional non-null types.3This 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 positionsAllows existing fields to use
!
(non-null) for error handling clients without breaking legacy clients by adding the@noPropagate
directive4Can be adopted gradually, field-by-field, or en masse by applying
@noPropagate
to all nullable positions.Can be removed from each field the moment no legacy clients query it
Footnotes​
-
Except solution 5 ↩
-
This is essentially the same as the
@semanticNonNull
directive, 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: ABORT
oronError: NO_PROPAGATE
, and thus will see the true introspection. Existing tooling doesn't know aboutonError
and 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_PROPAGATE
orABORT
) are unimpacted. ↩