Skip to main content

SemanticNonNull type (null only on error)

At a glance

Timeline


TL;DR: Introduces a new type wrapper, Semantic-Non-Null, which represents that a value will not be null unless an error happens, and if an error does happen then this null does not bubble.

The problem

GraphQL schema designers must use non-nullable types sparingly because if a non-nullable type were to raise an error then the entire selection set it is within will be destroyed, leading to clients receiving less usable data and making writing the results to a normalized cache a dangerous action. Because of this, nullable-by-default is a best practice in GraphQL, and non-null type wrappers should only be used for fields that the schema designer is confident will never raise an error - not just in the current schema, but in all future schemas.

Many GraphQL consumers choose to ignore the entire response from the server when any error happens, one reason for this is because the null bubbling behavior makes writing to normalized caches dangerous. For these users, when an error doesn't happen, the nullable fields they are dealing with can be frustrating because their type generation requires them to handle the null case even if it may never happen in practice, which can lead to a lot of unnecessary code that will never execute. There is currently no way for the type generators to know that a field will never be null unless there's an associated error.

The solution

We can categorise that there are effectively two types of null:

  • Error null: where a position is null and there's a related error (with matching or prefixed path) in the errors list - indicates that something went wrong.
  • Semantic null: where a position is null and there is no related error - this data truly is null (e.g. a user having not yet set their avatar may have avatar: null; this is not an error).

This PR introduces a new wrapper type in addition to List and Non-Null, called Semantic-Non-Null. The Semantic-Non-Null type indicates that the field will never be a semantic null - it will not be null in the normal course of business, but can be null only if accompanied by an error in the errors list (i.e. an "error null"). Thus a client that throws out all responses with errors will never see a null in this position. Also, critically, any null raised by this field will not bubble and thus if an error is found with the exact path to this null then it is safe to store the result (including the error) into a normalized cache.

In SDL the Semantic-Non-Null wrapper is currently represented by a ! prefix (as opposed to the ! suffix for a strict Non-Null).

Thus we have the following:

#Type descriptionSyntaxResult values
1Unadorned StringStringstring, or error null, or semantic null
2Semantic-Non-Null String!Stringstring, or error null
3(Strict-)Non-Null StringString!string

Note that 1 and 3 above are exactly the same as in the current GraphQL specification, this PR introduces 2 which sits in the middle.

Backwards compatibility

All existing schemas are automatically supported because the meaning of String and String! is unchanged.

To ensure that all existing clients are automatically supported, this PR introduces the includeSemanticNonNull argument on __Field.type which defaults to false. Clients that do not pass includeSemanticNonNull: true will see all Semantic-Non-Null types stripped, which will have the effect of making them appear as if they were the unadorned types. This is safe, since it means these clients will need to handle both error nulls and semantic nulls (as they traditionally would have) even though we know that a semantic null will never happen in practice.

All existing GraphQL documentation, tutorials, examples, and everything else we've built over the last 8 years remains valid since the meaning of String and String! are unchanged.

History

This PR is almost identical to #1048, but it changes the name of the new type wrapper from Null-Only-On-Error to Semantic-Non-Null, and changes the syntax from String* to !String. It addresses the True Nullability Schema discussion raised by @captbaritone and incorporates/adapts some of the terminology from @leebyron's Strict Semantic Nullability proposal.