Comments (6)
The server change you made was a breaking change.
we've suffered through versions of it at Facebook and it's a big part of why the default is nullable
It looks like the real problem was lack of automated BC breaking detector.
By making nullable
fields by default, you introduce another problem. In real life examples, nullable
is a special case ("server with images is down", "user has no referrer", etc.). If "special case" becomes a default one, then API users just won't pay that much attention. They always see data in the response, UI designer doesn't describe what happens if this field nullable, so it's very likely that client devs will start to just ignore nullable case.
If you put "the floor is slippery" sign everywhere and even when floor is dry, you don't make your place safer.
from graphql-spec.
In addition to @dschafer's points, another fairly significant reason we chose to default to nullable values is the relative cost of changing from one to the other once you have deployed a schema and have shipped clients which are consuming that schema.
Consider the case that non-null is the default, and you set up a schema for a comment discussion feature:
type Comment {
author: User
message: String
}
Then, you build Android and iOS apps, and launch them. A week later, you get bug reports that comments is not working for some users. You dig in and realize that if a user deleted their account, the comments they left behind are still there but there is no longer an author
to load, so your servers are returning an error and as a consequence entire comments views don't load if they include a comment from a deleted user.
Let's fix this by changing the schema so author
can return null.
type Comment {
author: User?
message: String
}
This gets deployed and you check back in with your users who are reporting this issue. They should see comments now, just without the author, right? Nope. Your users are now reporting that when they load a comment view, their app crashes. What? Null-pointer exception. You were indexing directly into that comment.author.picture.url
in your app code, and at the time, author
was non-nullable so that was safe. The server change you made was a breaking change. Yikes!
If the default had been nullable, then your original schema as defined would have Just Worked™.
For clients that understand nullability (Swift, recent Java and ObjC to a degree), they will warn you about accessing values without first checking for null. If you're checking null for something that you think should never be null, then you can have that conversation and go alter the schema. Perhaps you decide that there's always a message, an empty string can always suffice, so you change the schema to improve developer ergonomics on the client:
type Comment {
author: User
message: String!
}
Now we've ended up at the same result, but via the incremental application of becoming more specific rather than the late realization that you were too specific from the start and had to add breaking changes to correct. In fact, this change is totally safe. Any client that was previously checking for null will continue to work just fine.
This tale is slightly contrived, but we've suffered through versions of it at Facebook and it's a big part of why the default is nullable. As @dschafer pointed out, the probability that something will go wrong in the backend is more than 0 when loading data from storage, which is what GraphQL is designed to help you do.
In practice (perhaps unfortunately), most people do not think about nullability when designing their type systems. It's why most languages with strong types have no concept of non-null vs nullable with the obvious exception of those inspired by functional programming. And of course nearly all dynamically typed languages have no concept of this either. If most people do not think about nullability, we wanted to define the one that required less typing to be the safer of the two options, and let the incremental application of knowledge to allow for improvements from that position.
from graphql-spec.
In many programming languages this concept is implemented as not-null by default. ... I feel that nullable as a default can potentially confuse developers working with these languages
Many, but not the majority. I would contend that a non-null default would cause more confusion from programmers of more mainstream imperative languages and not used to thinking about this concept at all than a nullable default would cause confusion from those who use languages where they must think about nullability when defining types all the type because they have access to Option/Either/Maybe/?.
As a consequence of the first point, I noticed, that it can be pretty challenging to implement this concept in statically-typed languages, which natively support "maybe" types like Scala. ... and decided to introduce OptionType which replaces NotNullType in schema DSL ... Of course it still will produce correct introspection results
I strongly support this approach. Ultimately, the default for nullability is a moot-point when you have good GraphQL core libraries which respect the ergonomics of the particular language or environment. For strong typed languages that replace the concept of null with Maybe/Option/Either, I would expect to use a GraphQL library in exactly the way you define.
from graphql-spec.
This was the subject of considerable internal debate prior to the release of the spec, so it's definitely a discussion worth having, thanks for raising it! A few things came up when we were considering this that convinced us nullability by default was a reasonable call.
Because the type system on a GraphQL server is application-specific, not feature-specific, it can be difficult to provide universally valid default values. For example, what's the right behavior if someone's profile picture fails to load? In some features, it might be to replace it with a default silhouette; in others it might be to omit the person from the list. The correct behavior in the presence of an error is dependent not on the field, but on the feature, and only the client knows about the feature. By making the field be nullable and providing an error indicating that we nulled this field out because of the error, the feature can handle it as it sees fit.
Especially as queries and type systems grow, the probability that something will go wrong in the backend increases, and we need some way of handling that and expressing that in the query. The entirety of news feed for Facebook is powered by one GraphQL query, and there are a number of places where a backend system might (for any number of reasons) not be able to provide a result for a field. In a system where things are non-null by default, this could mean that a backend failure at a leaf node deep in the tree causes the entire query to fail.
To be clear, though: while nullable fields is a feature of GraphQL (and one that we think is valuable), it's absolutely not a requirement of it. Non-null fields and nullable fields are both first class citizens of GraphQL. We had to choose one to be the default, though, and based on the above, we chose nullable.
from graphql-spec.
Just wanted to add my 2 cents. This topic also concerned me quite a bit , so I glad that @matthewcheok started this discussion.
I definitely can see the advantages of nullable as a default. Backwards-compatibility and error-handling are good and valid arguments in favor of it. Still I feel that in many cases not-null default can be advantageous. In addition to @matthewcheok arguments (which I agree with), I also would like to point out these two aspects of it:
- In many programming languages this concept is implemented as not-null by default. I'm coming from scala background where nullable values are represented with
Option
type which serves as a container for nullable value. But scala is not the only example of it, languages like kotlin, groovy, c#, haskell, java, etc. all share the same approach to this issue. Even the flow type checker, which is heavily used in graphql-js itself, also takes this approach. I feel that nullable as a default can potentially confuse developers working with these languages (which on itself can case frustration or even hard-to-find bugs because of this mismatch) - As a consequence of the first point, I noticed, that it can be pretty challenging to implement this concept in statically-typed languages, which natively support "maybe" types like scala. I currently working on scala implementation of graphql. First I tried to implement not-null type for schema definition DSL, but at some point I gave up on this idea and decided to introduce OptionType which replaces
NotNullType
in schema DSL (it still work in progress, so things can change dramatically in future, but this what current state of implementation is). Of course it still will produce correct introspection results (with nullable default and not-null type, according to the spec)
from graphql-spec.
Closing this task now.
from graphql-spec.
Related Issues (20)
- GraphQL Mutation how to make fields optional to update fields only by selection and generate schema HOT 1
- [RFC] Non-existent request field operator HOT 8
- (Graphql-schema) Cannot query field "closeDiscussion" on type "Mutation" HOT 1
- GraphQL spec is contradictory: can selection sets be empty or not? HOT 1
- Allow to return Input Object types HOT 1
- Formalize Global Object Identification.
- Grammar should have single root node HOT 2
- [Feature request]: Support non-list variables for list arguments HOT 9
- Redundant field aliases identical to field names
- Document Level Directives HOT 7
- Coercing Variable Values when hasValue is not true and defaultValue does NOT exist HOT 7
- Strawman: mention of non-self-describing responses
- why not allow directive on field argument ? HOT 2
- On specifying ordered vs unordered enum definitions HOT 5
- Casting Error in GraphQL C# Library HOT 2
- Is there a reason graphql floats do not support Infinity and NaN? HOT 3
- Composite types is not clearly defined HOT 1
- Unclear spec for array with fragments HOT 1
- What must be the result of executing only one fragment on a list of union or interface type? HOT 3
- Interface conditions fragments may not be used across spreads, discouraging reusability HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphql-spec.