Uncoupling nullability and errors #85
Replies: 2 comments 6 replies
-
Thanks for raising this, Martin - it's really good to make sure that we evaluate every option and most importantly record that we have done so! It was essentially this desire that influenced the entire semantic non-null type in the first place - when discussing this at GraphQLConf 2023 it was clear that what we want for advanced clients like Relay with normalized caches was:
However, just making everything that's semantically non-nullable in the schema non-nullable is likely to significantly degrade existing applications (edit to add: by which I mean deployed clients that can no longer be updated, such as installed mobile apps) since where errors would previously have been handled gracefully, they no longer will be - instead causing selection sets, potentially over multiple levels, to be destroyed. Further, it's not clear that every type of client wants to disable null bubbling - some clients want to see the types in the These caveats led to the conclusion that we should have two schemas: one with null bubbling disabled, and one traditional. But maintaining two nearly-identical-except-for-nullability schemas is a chore. This is what influenced the semantic non-null discussion, and why my current preference is for the Aside: I think |
Beta Was this translation helpful? Give feedback.
-
I'm closing this as I'm not expecting any more feedback here. Let's consolidate the current state of discussions in https://github.com/graphql/graphql-wg/blob/main/rfcs/SemanticNullability.md |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
This is a proposal for a solution to semantic nullability that doesn't need to change the type system. It builds heavily on top of @benjie's directive proposal with an additional change to the GraphQL best practices.
Where can I use this?
Useful links:
@experimental_disableErrorPropagation
graphql.github.io#1970📜 Problem statement
Historically, GraphQL positions can be nullable for 2 reasons:
This coupling of nullability and errors is an issue for client developers because they cannot tell, looking at the schema, whether they need to handle
null
for a given position or whether the null is exceptional (an error).This proposal is an attempt to fix that situation from an "error" perspective, without changing the type system.
💡 Proposed solution
This proposal is twofold.
1. A new directive,
@disableErrorPropagation
@disableErrorPropagation
is based on @benjie's directive proposal, allowing clients to opt-out of error propagation:2. A change in the GraphQL best practices
Change the current recommendation from "use nullable by default" to "use semantic nullability by default":
!
liberally.null
usages in descriptions.You can preview the full changes in this matching pull request.
This change in best practices is made possible because error-aware clients can distinguish between
null
and error by using@experimental_disableErrorPropagation
and looking at theerrors
array. Most 2025 clients should be able to become error-aware clients and the best practices optimize for them.By removing the "nullable by default" best practice, schema authors can now expose the true nullability directly in the schema and the client developers make informed decisions.
🗺️ Migration path
In order to express semantic nullability in their schema outputs, an organization would have to:
@experimental_disableErrorPropagation
.update to an error-aware client now or be prepared to have a bigger blast radius for errors in N months
.T
toT!
That last point is a behaviour change for non error-handling clients as it changes the error boundaries. Each organization can decide how to mitigate for it fi they still want to support non error-handling clients.
🚀 Advantages
Simple
@experimental_disableErrorPropagation
is relatively simple to understand and implement. It's purely an execution change. By uncoupling nullability and errors, it brings clarity to the type system.Backward compatible
Existing queries continue working as they used to. Nothing changes by default. New clients can opt-out error propagation and get more precise responses. API providers can decide when they want to switch to a semantically nullable schema.
😐 Inconvenients
Still inconsistent with other languages
The unadorned type is still nullable, unlike in Kotlin and Swift. Schema authors need to remember to add the extra
!
.Non-error aware clients may see less partial data
Because schema authors are encouraged to use non-null types, the blast radius of an error may increase for a non-error handling client. See also questions below.
❓ Questions
Q: So GraphQL will always be inconsistent with Swift and Kotlin?
Maybe not but it's been really hard to converge on something that is also backward compatible. This is a simple, actionable proposal that doesn't require a big shift. (See also graphql/graphql-spec#63)
Q: Will large organizations with lots of clients be able to migrate?
If a large organization wants to change all their
String
toString!
, this has the side effect of increasing the blast radius of errors for non-error handling clients but:All in all, it's a tradeoff between simplicity and support for older clients. If you are part of a large organization considering this migration, please reach out!
Q: How big of a problem is it if non error-aware clients see less data?
Great question! I'n not aware of any case where it would be an issue, but I'm curious if anyone sees an issue there.
Q: Doesn't that proposal change the meaning of
String!
?With
@experimental_disableErrorPropagation
, any field may error whileString!
can be currently thought of as "never null and never an error".I would argue that that last part is not 100% correct because a resolver may still error in a non-null location but that error is hidden by the null in the ancestor. In practice, I'm not aware of clients that would break because of that.
Q: What about
ABORT
andINLINE
?@benjie's initial proposal included a full enum for possible error behaviours:
For simplicity, this proposal focuses on a single directive but if you have use cases for other enum values, please leave a comment below 👇.
📝 Changelog
A previous version of this proposal used
@onError(action: ErrorAction)
. This was discussed in graphql/graphql-js#4348 and we agreed to go with@disableErrorPropagation
for simplicity.Beta Was this translation helpful? Give feedback.
All reactions