Nulls in GraphQL: Cheatsheet
Tags: graphql, nullability, apollo, relay
Published: 2020-05-17 16:13:18

---
title: Nulls in GraphQL: Cheatsheet
published: true
date: 2020-05-14 22:32:07 UTC
tags: #GraphQL #Nullability #Apollo #Relay
canonical_url: https://hasura.io/blog/graphql-nulls-cheatsheet/
---

Nullability in GraphQL is a controversial topic. Some say constant null checks are a nuisance, while others err on the side of paranoia. I say, UFO's are real. But, that's not the point. In this post, you'll **hear both sides** of the discussion, find a **cheatsheet** you can refer to later, and read about different approaches to nulls and errors by **Apollo and Relay**.

- [Nullability and error handling in GraphQL](#nullability-and-error-handling-in-graphql)
- [Sending nulls in GraphQL queries](#nulls-in-the-query)
- [Receiving nulls in GraphQL responses](#nulls-in-the-response)
  - [Receiving null for a nullable field](#receiving-nullable)
  - [Receiving null for nullable parent of a non-nullable field](#receiving-non-nullable)
- [Finding the true meaning of null](#how-can-i-know-the-true-meaning-of-null)
- [Anatomy of GraphQL errors](#anatomy-of-the-errors-list)
- [Pros & cons](#pros-cons)
  - [Benefits of nullable types](#benefits-of-nullable-types)
  - [Benefits of Non-Null types](#benefits-of-non-null-types)
  - [Schema design and nulls](#so-how-should-i-design-my-schema)
- [Different approaches by GraphQL clients](#different-approaches-by-graphql-clients)
  - [Error handling in Apollo](#apollo-everything-is-possible)
  - [Error handling in Relay](#relay-all-is-good-all-the-time)

## Nullability and error handling in GraphQL

So what's all the ruckus about? It all starts with GraphQL's unconventional approach to defaults and error handling.

In other typed languages, like TypeScript, Flow, Rust, or Haskell, all types are non-nullable by default, and you opt into nullability. But **in GraphQL, all types are nullable by default, and you opt into non-nullability**. In fact, [`Non-Null` is a type](http://spec.graphql.org/June2018/#sec-Type-System.Non-Null) that wraps your original type when you opt in.

TypeScript:

```
interface User {
  name?: string // opting into optional name
  name: string | null // opting into nullable name
}
```

GraphQL:

```
type User {
  name: String! // opting into non-nullable name
}
```

As if that's not enough fodder for DRAMZ, GraphQL breaks with the REST convention of relying on HTTP status codes for error handling. Even if there's an error in the GraphQL layer while executing your request, the response is still  `200 OK` 😲

Then how does the client know about errors? This is where `null`s become relevant. The server returns a root field called `errors`, from which the client can extract them, and a root field called `data` that has all the requested fields. To top it off, **any fields with errors have the value `null`**.

All of this to say: all types in GraphQL are nullable by default in case of an error. GraphQL doesn't let a small hiccup get in the way of our data fetching: **when there's an error with a field, we still get partial data**.

We'll debate the pros and cons in a bit, including different approaches by Apollo and Relay. But first, the promised cheatsheet...


![](https://hasura.io/blog/content/images/2020/05/graphql-nulls-cheatsheet.png)

## Nulls in the query

A GraphQL query can have fields and inputs, with or without variables.

Fields are always optional, whether nullable or not.

```
type User {
  name: String! // non-null
  age: Int // nullable
}

{
  user {
    name // optional
    age // optional
  }
}
```

In inputs, such as field arguments, [nullable types are always optional and non‐null types are always required](http://spec.graphql.org/June2018/#sec-Type-System.Non-Null).

```
type User {
  // nullable filter input, non-null size input
  imageUrl(filter: String, size: String!): String
}

{
  user {
    imageUrl // not valid
    imageUrl(size: null) // not valid
    imageUrl(size: 'med') // valid
    imageUrl(filter: null, size: 'med') // valid
    imageUrl(filter: 'juno', size: 'med') // valid
  }
}
```

In addition, [a variable of a nullable type cannot be provided to a non‐null argument](http://spec.graphql.org/June2018/#example-6d129).

```
query user($imageSize: String) { // nullable variable
  imageUrl(size: $imageSize) // field with non-null argument
}
```

## Nulls in the response

If you get a `null` value for a nullable field from the server, there are four possibilities:

1. The value is actually null or absent, e.g. user doesn't exist
2. The value is hidden, e.g. user has blocked you (permission error)
3. There was another error fetching the value
4. An error or `null` value has bubbled up from a `Non-Null` child

### Wait, what was that about bubbling up?

The GraphQL spec says that a `null` result on a `Non-Null` type [bubbles up to the next nullable parent](http://spec.graphql.org/June2018/#sec-Errors). If this bubbling never stops because everything is of `Non-Null` type, then the root `data` field is `null`.

<!--kg-card-begin: html-->

Let's say `name` is nullable, but the resolver returns an error while fetching it:

<!--kg-card-end: html-->
```
data: { 
  user: { 
    name: null,
    age: 25,
    // other fields on user
 }
},
errors: [
  { 
    message: "Name could not be fetched",
    // ...
  }
]
```
<!--kg-card-begin: html-->

If `name` is non-nullable and `user` is nullable, this is what happens instead:

<!--kg-card-end: html-->
```
data: { 
  user: null
},
errors: [
  { 
    message: "Name could not be fetched",
    // ...
  }
]
```

When fields are nullable, as in the first example, you still get partial data: even if you don't have the name, you can still show the age and other fields. When you mark fields as `Non-Null`, as in the second example, you forfeit your right to partial data.

### How can I know the true meaning of `null`?

The four possibilities of `null` leave us with three questions:

1. Do we have an actual absent value or an error?
2. If it's an error, which field is it on?
3. What kind of error is it?

And the answers lie in the `errors` list returned in the response.

> If an error is thrown while resolving a field, it should be treated as though the field returned `null`, and an error must be added to the `errors` list in the response.   
> - [GraphQL Spec](http://spec.graphql.org/June2018/#sec-Errors-and-Non-Nullability)

### Anatomy of the `errors` list

The `errors` list must include `message`, `locations`, and `path` entries, with an optional `extensions` entry.

```
"errors": [
  {
    // error message from the resolver
    "message": "Name for user with ID 42 could not be fetched.",
    
    // map to the field in the GraphQL document
    "locations": [{ "line": 3, "column": 5 }],
    
    // path to the field
    "path": ["user", 0, "name"],
    
    // optional map with additional info
    "extensions": {
      code: PERMISSION_ERROR,
      weatherOutside: 'weather'
    }
  }
]
```

The `path` entry answers our first two questions: whether the `null` result is intentional or due to an error, and what field it's on. The `extensions` entry answers our third question by allowing us to specify the type of error, and anything else we can possibly divine: the time of day when it happened, the weather outside, etc.

<iframe width="480" height="270" src="https://www.youtube.com/embed/Oj37OwvKqpA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><figcaption>Paul Rudd &amp; Jason Segel in "Forgetting Sarah Marshall"</figcaption>

In the case of a  `Non-Null` field, the `path` entry still specifies the correct source of the error, even when the `data` returned points to the parent as the troublemaker.

```
data: { 
  user: null
},
errors: [
  { 
    message: "Name could not be fetched",
    path: ["user", 0, "name"]
    // ...
  },
  // ...
]
```

# Pros & cons

Now that we've got the cheatsheet down, let's talk about the pros and cons of nullable vs `Non-Null` types.

## Benefits of nullable types

We've already covered one major benefit:

- When the HTTP status code is `200 OK`, we're able to get **partial data** from the server, despite errors on specific fields. But we still need a way to tell if something went wrong, which we can achieve with a `null` value on the erroneous field, along with an `errors` list (Btw, we'll discuss another solution to this – returning an `Error` type instead of `null` – in the Relay section below).

Other benefits:

- **Privacy** when you want to obfuscate the reasons for `null`. Maybe you don't want the client to know whether you got a `null` on `user` because the user has blocked you or because the user simply doesn't exist. If you're a ghoster, this is the way to go.
- If you're serving different clients from a single schema, nullable fields are more reliable, and **easier to evolve**. For example, if you remove a `Non-Null` field from the schema, a client that you have no control ever may break when it receives `null` for that field.
- **Coding defensively** on the client side. Null check all the things!

```
if (user && user.location) return user.location.city;
```

## Benefits of `Non-Null` types

If you find null checks cumbersome,  `Non-Null` is the type for you. Why?

- You get **guaranteed values**. If you know that `location` is non-nullable, you can just do this:

```
if (user) return user.location.city;
```

If location returns `null` it will bubble up to `user`, and `user.location.city` will never execute. `location` is guaranteed to never be `null`, so you never need a null check on it.

- You can combine `Non-Null` types with **query type generation** to make your code even more predictable. For example, with TypeScript, the generated code for a users query can be:

```
type GetUsersQuery = {
  users: Array<{
    __typename: "User",
    name: string // if Non-Null in schema
    name: string | null // if nullable in schema
  }
};
```

- **Easy error detection**. If you get a `null` value for a non-nullable field, you know it's because of an error, since it can't be a legitimate absent value.

## So how should I design my schema?

Though this is a hotly debated topic, the general recommendation is to make everything nullable except when it doesn't make sense, e.g. `getUserById(id: ID!)`, or the value is guaranteed to exist, e.g. a primary key like `id`. But you can weigh the pros and cons above, and decide for yourself! (If you do, please let me know in the comments).

# Different approaches by GraphQL clients

Depending on what GraphQL client you're using, you may be able to pick your strategy on a per-query basis, or come up with a different solution to error handling altogether.

## Apollo: Everything is possible

When it comes to error handling, [Apollo gives you options](https://www.apollographql.com/docs/react/data/error-handling). Three options, in fact.

- `none` (default): Treat GraphQL errors like network errors, ignore data
- `ignore`: Get data, ignore errors
- `all`: Get both data & errors

The `all` option matches the GraphQL spec, and will allow you to get partial data in case of an error. You can set the `errorPolicy` on a per-query basis, like so:

```
const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: 'all' });
```

## Relay: All is good all the time

Relay is all about living the good life. A little GraphQL error here and there isn't enough to mess with Relay's zen thing.

That's right. [Relay ignores GraphQL errors](https://relay.dev/docs/en/a-guided-tour-of-relay#accessing-errors-in-graphql-response) unless:

- the fetch function provided to the [Relay Network](https://relay.dev/docs/en/network-layer) throws or returns an Error
- the top-level `data` field isn't returned in the response

The Relay docs recommend modeling errors in your schema rather than returning `null`.

```
type Error {
  message: String!
}

type User {
  name: String | Error
}
```

For a detailed breakdown of how you might model this in your schema, check out [this excellent blog post](https://blog.logrocket.com/handling-graphql-errors-like-a-champ-with-unions-and-interfaces/) by Laurin Quast.

![Error handling: GraphQL spec vs Apollo vs Relay](https://hasura.io/blog/content/images/2020/05/graphql-nulls.png)

## Conclusion

I hope this article has helped you master nulls in GraphQL. We covered:

- GraphQL's approach to nullability and error handling
- What nulls mean in GraphQL queries and responses
- Pros & cons of nullable and Non-Null types
- Different approaches to nulls and error handling by Apollo and Relay

Where do you stand in this discussion? What has been your experience with nullability? Let us know on [Twitter](https://twitter.com/intent/tweet?text=Hey%2C%20%40sez%20and%20%40HasuraHQ%2C%20regarding%20the%20blog%20post%20https%3A%2F%2Fbit.ly%2F2WsoqXt%2C%20) or in the comments below!

## Further Reading

- [Using nullability in GraphQL](https://www.apollographql.com/blog/using-nullability-in-graphql-2254f84c4ed7), by [Sashko Stubailo](https://twitter.com/stubailo)
- [Nullability in GraphQL](https://medium.com/expedia-group-tech/nullability-in-graphql-b8d06fbd8a3c), by [Grant Norwood](https://twitter.com/grantnorwood)
- [When To Use GraphQL Non-Null Fields](https://medium.com/@calebmer/when-to-use-graphql-non-null-fields-4059337f6fc8), by [Caleb Meredith](https://twitter.com/calebmer)
- [Handling GraphQL errors like a champ with unions and interfaces](https://blog.logrocket.com/handling-graphql-errors-like-a-champ-with-unions-and-interfaces/), by [Laurin Quast](https://twitter.com/n1rual)
- [GraphQL Best Practices: Nullability](https://graphql.org/learn/best-practices/#nullability)
- [GraphQL Spec](http://spec.graphql.org)
- [Handling errors with Apollo](https://www.apollographql.com/docs/react/data/error-handling/)
- [Relay: Accessing errors in GraphQL Response](https://relay.dev/docs/en/a-guided-tour-of-relay#accessing-errors-in-graphql-response)