Launch GraphOS Studio



đź’ˇ TIP

If you're an enterprise customer looking for more material on this topic, try the Enterprise best practices: Schema design course on Odyssey.

Not an enterprise customer? Learn about GraphOS for Enterprise.

Make intentional choices about nullability

All s in GraphQL are nullable by default and it's often best to err on the side of embracing that default behavior as new fields are initially added to a . However, where warranted, non-null fields and s (denoted with a trailing !) are an important mechanism that can help improve the expressiveness and predictability of a . Non-null s can also be a win for clients because they will know exactly where to expect values to be returned when handling query responses. Non-null fields and s do, of course, come with trade-offs, and it's important to weigh the implications of each choice you make about nullability for every type, field, and argument in a schema.

Plan for backward compatibility

Null values can happen for multiple reasons like authentication failures, database error and/or network errors. Where possible, client teams should be included in conversations about nullability in design. This will help them prepare for handling null values in way that doesn't detract from the experience of the end user in the front-end interface. Null can also be a valid non-error value. In these cases, indicating it in the field description will help the client teams make informed decisions.

While it's important to make informed decisions about nullability when initially designing a service's , you will inevitably be faced with making a breaking change of this nature as a schema naturally evolves. When this happens, 's field usage data can give you insight into how those s are used currently in different s and across different clients. This visibility will help you identify issues proactively and allow you to communicate these changes to impacted clients in advance so they can avoid unexpected errors.

Minimize nullable arguments and input fields

As mentioned previously, converting a nullable or input for a to non-null may lead to breaking changes for clients. As a result, specifying non-null arguments and input fields on mutations can help you avoid this breaking change scenario in the future. Doing so, however, will typically require that you design finer-grained mutations and avoid using "everything but the kitchen sink" input types as arguments that are filled with nullable fields to account for all possible use cases.


# Two nullable arguments is ambiguous — what happens if we provide both? None?
type Query {
user(id: ID, username: String): User


# Separate fields with non-nullable arguments is clearer, and it's easier to
# evolve because converting a non-nullable argument to nullable is not a
# breaking change.
type Query {
userById(id: ID!): User
userByUsername(username: String!): User

This approach also enhances the overall expressiveness of the and provides more transparency in your observability tools about how s impact overall performance (this is especially true for queries). What's more, it also shifts the burden away from graph consumers to guess exactly which s need to be included in a to achieve their desired result.

And as an additional tip when you do use nullable s and input s, consider providing a default value to improve the overall expressiveness of a by making default behaviors more transparent. In this example, we can improve the type by adding an ALL value to its corresponding ProductType enum and setting the default value to ALL. As a result, we no longer need to provide specific directions about this behavior in the 's description string:

type Query {
"Fetch a paginated list of products based on a filter."
# ...
"Filter products based on a type."
type: ProductType = ALL
): ProductConnection
enum ProductType {

Weigh the implications of non-null entity references

When adding s to a that are resolved with data from third-party s, the conventional advice is to make these fields nullable given the potential for the request to fail or for the data source to make breaking changes without warning. Federated graphs add an interesting dimension to these considerations given that many of the entities in the graph may be backed by data sources that are not in a given service's immediate control.

The matter of whether you should make referenced entities nullable in a 's will depend on your existing architecture and internal SLAs and will likely need to be assessed on a case-by-case basis. Keep in mind the implication that nullability has on error handling—specifically, when a value cannot be resolved for a non-null , then the null result bubbles up to the nearest nullable parent—and consider whether it's better to have a partial result or no result at all if a request for an entity fails.

Edit on GitHubEditForumsDiscord