This post by Caleb Meredith does a good job of explaining why non-null fields should often be avoided https://medium.com/@calebmer/when-to-use-graphql-non-null-fields-4059337f6fc8
This issue lists all places where Graphcool could expose non-null fields and provides the rationale for either exposing them as nullable or non-null fields.
GraphQL schemas are often meant to be consumed by multiple applications, sometimes created by independent teams inside or outside the company. This makes it extremely difficult to change the schema in a way that could break application logic. Making a non-null field nullable is such a change.
Graphcool will usually be fronted by a single server that transforms the schema, performs authorization etc, somewhat alleviating this point.
A GraphQL API will never return null for a non-null field. Instead the null value bubbles up the hierarchy to the nearest nullable field.
If a field has the type [Int!] and the returned list contains a single null, the entire field will be null, even if the list contained valid values.
Graphcool is in control of data going into the database, so we can make smart decisions about when it is safe to expose a non-null field. This is somewhat complicated by the fact that we allow application developers direct access to the database, so it is possible for the underlying data to become invalid at any point.
Consider this schema
type Post {
id: ID! @unique
title: String!
subTitle: String
comments: [Comment]
author: User!
}
# User and Comment types omitted for brevity
All data fields are optional
required scalar fields are required:
input UserCreateInput {
title: String!
subTitle: String
# relation fields
}
All nested mutation fields are optional
required relation fields are required:
input UserCreateInput {
comments: CommentCreateManyInput
author: UserCreateOneInput!
# scalar fields
}
As detailed in #1341 the fields in nested mutation types are all optional, as there are many valid options. We return a runtime error if these fields are used in invalid combinations.
Note that only one-relations can be required.
The above Post type generates the following type:
type Post {
id: ID! @unique
title: String!
subTitle: String
comments: [Comment]
commentsConnection: CommentConnection!
author: User
}
The underlying database guarantees that required fields are non-null, so we can safely expose this.
The one exception is that data written directly to the database might have a format that is incompatible with the GraphQL type. When this happens, the database is considered to be in an inconsistent state. We will introduce tools to help developers to recover from this situation.
Required relations are not enforced by the underlying database, so at this time we should not make the field required.
We make the connection field required and guarantee that it will always be returned successfully. All sub fields are optional except edges which is guaranteed to always be there. The Edge is optional, preventing any null bubbling.
On the Edge type, both node and cursor fields are non-null. If for any reason we fail to return either of them, we simply bubble the error up to the optional Edge.
type CommentConnection {
edges: [CommentEdge]!
aggregate: CommentAggregate
group: CommentGroupBy
}
type CommentEdge {
node: User!
cursor: String!
}
For the reasons laid out by caleb we will make elements in list relations optional. This prevents a single invalid node to bubble up and render the entire list unavailable:
comments: [Comment]!
Ideally we would like to also make the node in the list required, and we might choose to implement this change in the future:
comments: [Comment!]! # We might adopt this in the future
I think Caleb Meredith actually did a terrible job with his reasoning about non-null fields, and I don't agree with any of the arguments.. Apart from that, even though some users might adapt some of the points from that article, I see absolutely no reason why the generated schema should deviate from what I, the developer, specify on my type definitions.
If I specify author: User!, why would the generated schema have author: User. If I wanted it to be nullable in the first place, I would have put author: User in my type definition instead. It's also very inconsistent, because the author field on the input type is required: author: UserCreateOneInput!.
I see this is no closed, after being tagged for beta2, but without any information on what was decided.
I'd also like to know what is the plan here. I think non-null is a feature that developers should be able to chose to use if they understand the tradeoffs however Prisma could make improvements on the null values handling in non-null fields, for example:
Returning null for an entire array when just one item has an unexpected null value is not what I'd expect as a developer, instead I'd expect the array to contain the valid items and maybe highlight that some specific item encountered a fatal error.
You can use the xsConnection queries, that expose an required edges array of optional nodes. If one node is null, that doesn't "propograte" to the entire list in this case.
Does that resolve your problem, @tomsdev?
Most helpful comment
I think Caleb Meredith actually did a terrible job with his reasoning about non-null fields, and I don't agree with any of the arguments.. Apart from that, even though some users might adapt some of the points from that article, I see absolutely no reason why the generated schema should deviate from what I, the developer, specify on my type definitions.
If I specify
author: User!, why would the generated schema haveauthor: User. If I wanted it to be nullable in the first place, I would have putauthor: Userin my type definition instead. It's also very inconsistent, because theauthorfield on the input type is required:author: UserCreateOneInput!.