Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free

Nullability

Learn how to make intentional choices about nullability

schema-designfederation

💡 TIP

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

Not an enterprise customer? Learn about GraphOS for Enterprise.

Make intentional choices about nullability

All in 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 schema. However, where warranted, non-null fields and (denoted with a trailing !) are an important mechanism that can help improve the expressiveness and predictability of a schema. Non-null fields can also be a win for clients because they will know exactly where to expect values to be returned when handling responses. Non-null fields and arguments 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 schema 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 frontend 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 schema, 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 fields are used currently in different 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 field 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 schema and provides more transparency in your observability tools about how arguments impact overall performance (this is especially true for queries). What's more, it also shifts the burden away from consumers to guess exactly which fields need to be included in a mutation to achieve their desired result.

And as an additional tip when you do use nullable arguments and input fields, consider providing a default value to improve the overall expressiveness of a schema by making default behaviors more transparent. In this example, we can improve the type argument 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 argument's description string:

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

Weigh the implications of non-null entity references

When adding fields to a schema that are resolved with data from third-party , 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 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 schema 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 field, 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 fails.

Next
Home
Rate articleRateEdit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company