# Handling nullability and partial data

Make your queries even more typesafe

---

**EXPERIMENTAL**

**Nullability annotations are currently [experimental](/content/docs/resources/product-launch-stages/#experimental-features/index.html) in Apollo Kotlin.** If you have feedback on them, please let us know via [GitHub issues](https://github.com/apollographql/apollo-kotlin/issues/new?assignees=&labels=Type%3A+Bug&template=bug_report.md), in the [Kotlin Slack community](https://slack.kotl.in/), or in the [GraphQL nullability working group](https://github.com/graphql/nullability-wg/).

## Introduction

_This section is a high level description of nullability and errors in GraphQL and the problem it causes for app developers. For the proposed solution, skip directly to the [@semanticNonNull](/content/docs/kotlin/advanced/nullability#semanticnonnull/index.html) section._

GraphQL does not have a `Result` type. If a field errors, it is set to `null` in the JSON response and an error is added to the `errors` array.

From the schema alone, it is impossible to tell if `null` is a valid business value or only happens for errors:

### GraphQL

```
1type User {
2  id: ID!
3  # Should the UI deal with user without a name here? It's impossible to tell.
4  name: String
5  avatarUrl: String
6}
```

The GraphQL best practices recommend making fields nullable by default to account for errors.

From [graphql.org](https://graphql.org/learn/best-practices/#nullability):

> _In a GraphQLtype system, every field is nullable by default._
> _This is because there are many things that can go awry in a networked_
> _service backed by databases and other services._

For an example, the following query:

### GraphQL

```
1query GetUser {
2  user {
3    id
4    name
5    avatarUrl
6  }
7}
```

receives a response like so in the case of an error:

### JSON

```
1{
2  "data": {
3    "user": {
4      "id": "1001",
5      "name": null,
6      "avatarUrl": "https://example.com/pic.png"
7    }
8  },
9  "errors": [\
10    {\
11      "message": "Cannot resolve user.name",\
12      "path": ["user", "name"]\
13    }\
14  ]
15}
```

This nullable default has one major drawback for frontend developers. It requires to carefully check every field in your UI code.

Sometimes it's not clear how to handle the different cases:

### Kotlin

```
1@Composable
2fun User(user: GetUserQuery.User) {
3  if (user.name != null) {
4    Text(text = user.name)
5  } else {
6    // What to do here?
7    // Is it an error?
8    // Is it a true null?
9    // Should I display a placeholder? an error? hide the view?
10  }
11}
```

When there are a lot of fields, handling the `null` case on every one of them becomes really tedious.

Apollo Kotlin offers nullability directives to deal with this situation:

- [`@semanticNonNull`](/content/docs/kotlin/advanced/nullability#semanticnonnull/index.html) (type-system directive)
- [`@catch`](/content/docs/kotlin/advanced/nullability#handle-errors-and-receive-partial-data-with-catch/index.html) (executable directive)

These tools change the GraphQL default from "handle every field error" to "opt-in the errors you want to handle".

## Enabling error aware parsing

To make the Apollo generated parsers aware of errors, import the [nullability directives](https://specs.apollo.dev/nullability/v0.4/) using the [`@link` directive](https://specs.apollo.dev/link/v1.0/):

### GraphQL

```
1extend schema @link(
2  url: "https://specs.apollo.dev/nullability/v0.4",
3  import: ["@semanticNonNull", "@semanticNonNullField", "@catch", "CatchTo", "@catchByDefault"]
4)
```

And define the default behavior when an error happens.

You may catch the error and expose it as a `FieldResult`:

### GraphQL

```
1# Catch the error and expose it as a FieldResult<T> in the generated models.
2extend schema @catchByDefault(to: RESULT)
```

or re-throw the error:

### GraphQL

```
1# Re-throw the error. If no parent field catches it, `response.exception` contains an instance of `ApolloGraphQLException`.
2extend schema @catchByDefault(to: THROW)
```

or coerce the error to `null`, like the current GraphQL default:

### GraphQL

```
1# Coerce the error to null. The caller must read `response.errors` to disambiguate a null vs error field.
2extend schema @catchByDefault(to: NULL)
```

Adding `@catchByDefault(to: NULL)` is a no-op for codegen that unlocks using `@catch` in your operations.

Because errors can never happen on non-null fields (`String!` and others), `@catchByDefault` only influences the nullable fields in your schema.

For fields that are only nullable for error reasons, Apollo Kotlin supports `@semanticNonNull`.

## `@semanticNonNull`

`@semanticNonNull` introduces a new type in the GraphQLtype system.

A `@semanticNonNull` type can never be null **except** if there is an error in the errors array.

Define a field as semantically non null by adding the `@semanticNonNull`directive on its definition:

### GraphQL

```
1type User {
2  id: ID!
3  # name is never null unless there is an error
4  name: String @semanticNonNull
5  # avatarUrl may be null even if there is no error. In that case the UI should be prepared to display a placeholder.
6  avatarUrl: String
7}
```

**note**

`@semanticNonNull` is a directive so that it can be introduced without breaking the current GraphQL tooling but the ultimate goal is to introduce new syntax. See the [nullability working group discussion](https://github.com/graphql/nullability-wg/discussions/58) for more details.

For fields of `List` type, `@semanticNonNull` applies only to the first level. If you need to apply it to a given level, use the `levels`argument:

### GraphQL

```
1type User {
2  # adminRoles may be null if the user is not an admin
3  # if the user is an admin, adminRoles[i] is never null unless there is also an error
4  adminRoles: [AdminRole] @semanticNonNull(levels: [1])
5}
```

With `@semanticNonNull`, a frontend developer knows that a given field is never null unless there is an error and can therefore act accordingly. The Apollo Kotlin codegen generates `@semanticNonNull`fields as non-null Kotlin properties. No need to guess anymore!

Ideally, your backend team annotates their schema with `@semanticNonNull`directives so that different frontend teams can benefit from the new type information.

Sometimes this process takes time.

For these situations, extend your schema by using `@semanticNonNullField` in your extra.graphqls file:

### GraphQL

```
1# Same effect as above but works as a schema extensions
2extend type User @semanticNonNullField(name: "name")
```

You can later share that file with your backend team and double-check that your interpretation of the types is the correct one.

## `@catch`

While `@semanticNonNull` is a server directive that describes your data, `@catch` is a client directive that defines how to handle errors.

`@catch` allows to:

- handle errors as `FieldResult<T>`, getting access to the colocated error.
- throw the error and let another parent field handle it or bubble up to `data == null`.
- coerce the error to `null` (current GraphQL default).

For fields of `List` type, `@catch` applies only to the first level. If you need to apply it to a given level, use the `levels`argument:

### GraphQL

```
1query GetUser {
2  user {
3    # map friends[i] to FieldResult
4    friends @catch(to: RESULT, levels: [1])
5  }
6}
```

### Colocate errors

To get colocated errors, use `@catch(to: RESULT)`:

### GraphQL

```
1query GetUser {
2  user {
3    id
4    # map name to FieldResult<String> instead of stopping parsing
5    name @catch(to: RESULT)
6  }
7}
```

The above query generates the following Kotlin code:

### Kotlin

```
1class User(
2    val id: String,
3    // note how String is not nullable. This is because name
4    // was marked `@semanticNonNull` in the previous section.
5    val name: FieldResult<String>,
6)
```

Use `getOrNull()` to get the value:

### Kotlin

```
1println(user.name.getOrNull()) // "Luke Skywalker"
2// or you can also decide to throw on error
3println(user.name.getOrThrow())
```

And `graphQLErrorOrNull()` to get the colocated error:

### Kotlin

```
1println(user.name.graphQLErrorOrNull()) // "Cannot resolve user.name"
```

### Throw errors

To throw errors, use `@catch(to: THROW)`:

### GraphQL

```
1query GetUser {
2  user {
3    id
4    # throw any error
5    name @catch(to: THROW)
6  }
7}
```

The above query generates the following Kotlin code:

### Kotlin

```
1class User(
2    val id: String,
3    val name: String,
4)
```

**note**

The error is thrown during parsing but still caught before it reaches your UI code. If no parent field catches it, the Apollo Kotlin runtime will and set it as `ApolloResponse.exception`.

### Coerce errors to null

To coerce errors to `null` (current GraphQL default), use `@catch(to: NULL)`:

### GraphQL

```
1query GetUser {
2  user {
3    id
4    # coerce errors to null
5    name @catch(to: NULL)
6  }
7}
```

The above query generates the following Kotlin code:

### Kotlin

```
1class User(
2    val id: String,
3    // Note how name is nullable again despite being marked
4    // @semanticNonNull in the schema
5    val name: String?,
6)
```

**note**

The error is thrown during parsing but still caught before it reaches your UI code. If no parent field catches it, the Apollo Kotlin runtime does and exposes the exception in `ApolloResponse.exception`.

## Migrate to semantic nullability

Semantic nullability is the most useful for schemas that are nullable by default. These are the schemas that require "handling every field error".

In order to change that default to "opt-in the errors you want to handle", you can use the following approach:

1. import the nullability directives.
2. Default to coercing to null: `extend schema @catchByDefault(to: NULL)`. This is a no-op to start exploring the directives.
3. Add `@catch` to individual fields, get more comfortable with how it works.
4. When ready to do the big switch, change to `extend schema catchByDefault(to: THROW)` and (at the same time) add `query GetFoo @catchByDefault(to: NULL) {}` on all operations/fragments (this is a no-op).
5. From this moment on, new queries written throw on errors by default.
6. Remove `query GetFoo @catchByDefault(to: NULL) {}` progressively.

## Migrate from `@nonnull`

If you were using `@nonnull` before, you can now use `@semanticNonNull`.

`@semanticNonNull`, coupled with `@catch` is more flexible and also more in line with other frameworks.

Catch to NULL by default:

### GraphQL

```
1extend schema @link(
2  url: "https://specs.apollo.dev/nullability/v0.4",
3  import: ["@semanticNonNull", "@semanticNonNullField", "@catch", "CatchTo", "@catchByDefault"]
4)
5
6extend schema @catchByDefault(to: NULL)
```

Replace `@nonnull` with `@semanticNonNullField`:

### GraphQL

```
1# Replace
2extend type Foo @nonnull(fields: "bar")
3
4# With
5extend type Foo @semanticNonNullField(name: "bar")
```

In your queries, use `@catch(to: THROW)` to generate those fields as non-nullable:

### GraphQL

```
1# Add `@catch(to: THROW)`
2query GetFoo {
3  foo @catch(to: THROW)
4}
```

**For usages in executable documents**

Because nullability is a schema concern, `@semanticNonNull` cannot be used in executable documents. Instead, define your fields as `@semanticNonNull`:

### GraphQL

```
1# Replace
2query GetFoo {
3  foo @nonnull
4}
5
6# With
7query GetFoo {
8  foo @catch(to: THROW)
9}
10
11# and make foo `@semanticNonNull` in your schema
12extend type Query @semanticNonNullField(name: "foo")
```
