|
| 1 | +--- |
| 2 | +title: Nullability |
| 3 | +sidebarTitle: Nullability in GraphQL.js |
| 4 | +--- |
| 5 | + |
| 6 | +# Nullability in GraphQL.js |
| 7 | + |
| 8 | +Nullability is a core concept in GraphQL that affects how schemas are defined, |
| 9 | +how execution behaves, and how clients interpret results. In GraphQL.js, |
| 10 | +nullability plays a critical role in both schema construction and |
| 11 | +runtime behavior. |
| 12 | + |
| 13 | +This guide explains how nullability works, how it's represented in GraphQL.js, |
| 14 | +and how to design schemas with nullability in mind. |
| 15 | + |
| 16 | +## How nullability works |
| 17 | + |
| 18 | +In GraphQL, fields are nullable by default. This means if a resolver function |
| 19 | +returns `null`, the result will include a `null` value unless the field is |
| 20 | +explicitly marked as non-nullable. |
| 21 | + |
| 22 | +When a non-nullable field resolves to `null`, the GraphQL execution engine |
| 23 | +raises a runtime error and attempts to recover by replacing the nearest |
| 24 | +nullable parent field with `null`. This behavior is known |
| 25 | +as null bubbling. |
| 26 | + |
| 27 | +Understanding nullability requires familiarity with the GraphQL type system, |
| 28 | +execution semantics, and the trade-offs involved in schema design. |
| 29 | + |
| 30 | +## The role of `GraphQLNonNull` |
| 31 | + |
| 32 | +GraphQL.js represents non-nullability using the `GraphQLNonNull` wrapper type. |
| 33 | +By default, all fields are nullable: |
| 34 | + |
| 35 | +```js |
| 36 | +import { |
| 37 | + GraphQLObjectType, |
| 38 | + GraphQLString, |
| 39 | + GraphQLNonNull, |
| 40 | +} from 'graphql'; |
| 41 | + |
| 42 | +const UserType = new GraphQLObjectType({ |
| 43 | + name: 'User', |
| 44 | + fields: () => ({ |
| 45 | + id: { type: new GraphQLNonNull(GraphQLString) }, |
| 46 | + email: { type: GraphQLString }, |
| 47 | + }), |
| 48 | +}); |
| 49 | +``` |
| 50 | + |
| 51 | +In this example, the `id` field is non-nullable, meaning it must always |
| 52 | +resolve to a string. The `email` field is nullable. |
| 53 | + |
| 54 | +You can use `GraphQLNonNull` with: |
| 55 | + |
| 56 | +- Field types |
| 57 | +- Argument types |
| 58 | +- Input object fields |
| 59 | +- Return types for resolvers |
| 60 | + |
| 61 | +You can also combine it with other types to create more |
| 62 | +specific constraints. For example: |
| 63 | + |
| 64 | +```js |
| 65 | +new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(UserType))) |
| 66 | +``` |
| 67 | + |
| 68 | +This structure corresponds to [User!]! in SDL: a non-nullable list of non-null |
| 69 | +`User` values. When reading code like this, work from the inside out: `UserType` |
| 70 | +is non-nullable, and wrapped in a list, which is itself non-nullable. |
| 71 | + |
| 72 | +## Execution behavior |
| 73 | + |
| 74 | +GraphQL.js uses nullability rules to determine how to handle `null` values |
| 75 | +at runtime: |
| 76 | + |
| 77 | +- If a nullable field returns `null`, the result includes that field with |
| 78 | +a `null` value. |
| 79 | +- If a non-nullable field returns `null`, GraphQL throws an error and |
| 80 | +sets the nearest nullable parent field to `null`. |
| 81 | + |
| 82 | +This bubbling behavior prevents partial data from being returned in cases where |
| 83 | +a non-nullable guarantee is violated. |
| 84 | + |
| 85 | +Here's an example that shows this in action: |
| 86 | + |
| 87 | +```js |
| 88 | +import { |
| 89 | + GraphQLSchema, |
| 90 | + GraphQLObjectType, |
| 91 | + GraphQLString, |
| 92 | + GraphQLNonNull, |
| 93 | +} from 'graphql'; |
| 94 | + |
| 95 | +const UserType = new GraphQLObjectType({ |
| 96 | + name: 'User', |
| 97 | + fields: { |
| 98 | + id: { type: new GraphQLNonNull(GraphQLString) }, |
| 99 | + }, |
| 100 | +}); |
| 101 | + |
| 102 | +const QueryType = new GraphQLObjectType({ |
| 103 | + name: 'Query', |
| 104 | + fields: { |
| 105 | + user: { |
| 106 | + type: UserType, |
| 107 | + resolve: () => ({ id: null }), |
| 108 | + }, |
| 109 | + }, |
| 110 | +}); |
| 111 | + |
| 112 | +const schema = new GraphQLSchema({ query: QueryType }); |
| 113 | +``` |
| 114 | + |
| 115 | +In this example, the `user` field returns an object with `id: null`. |
| 116 | +Because `id` is non-nullable, GraphQL can't return `user.id`, and instead |
| 117 | +nullifies the `user` field entirely. An error describing the violation is |
| 118 | +added to the `errors` array in the response. |
| 119 | + |
| 120 | +## Schema design considerations |
| 121 | + |
| 122 | +Using non-null types communicates clear expectations to clients, but it's |
| 123 | +also less forgiving. When deciding whether to use `GraphQLNonNull`, keep |
| 124 | +the following in mind: |
| 125 | + |
| 126 | +- Use non-null types when a value is always expected. This reflects intent |
| 127 | +and reduces ambiguity for clients. |
| 128 | +- Avoid aggressive use of non-null types in early schema versions. It limits |
| 129 | +your ability to evolve the API later. |
| 130 | +- Be cautious of error bubbling. A `null` return from a deeply nested non-nullable |
| 131 | +field can affect large portions of the response. |
| 132 | + |
| 133 | +### Versioning |
| 134 | + |
| 135 | +Non-null constraints are part of a field's contract: |
| 136 | + |
| 137 | +- Changing a field from non-nullable to nullable is a breaking change. |
| 138 | +- Changing from nullable to non-nullable is also breaking unless you can |
| 139 | +guarantee that the field will never return `null`. |
| 140 | + |
| 141 | +To reduce the risk of versioning issues, start with nullable fields and add |
| 142 | +constraints as your schema matures. |
| 143 | + |
| 144 | +## Using `GraphQLNonNull` in schema and resolvers |
| 145 | + |
| 146 | +Let's walk through two practical scenarios that show how GraphQL.js enforces |
| 147 | +nullability. |
| 148 | + |
| 149 | +### Defining a non-null field |
| 150 | + |
| 151 | +This example defines a `Product` type with a non-nullable `name` field: |
| 152 | + |
| 153 | +```js |
| 154 | +import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from 'graphql'; |
| 155 | + |
| 156 | +const ProductType = new GraphQLObjectType({ |
| 157 | + name: 'Product', |
| 158 | + fields: () => ({ |
| 159 | + name: { type: new GraphQLNonNull(GraphQLString) }, |
| 160 | + }), |
| 161 | +}); |
| 162 | +``` |
| 163 | + |
| 164 | +This configuration guarantees that `name` must always be a string |
| 165 | +and never `null`. If a resolver returns `null` for this field, an |
| 166 | +error will be thrown. |
| 167 | + |
| 168 | +### Resolver returns `null` for a non-null field |
| 169 | + |
| 170 | +In this example, the resolver returns an object with `name: null`, violating |
| 171 | +the non-null constraint: |
| 172 | + |
| 173 | +```js |
| 174 | +import { |
| 175 | + GraphQLObjectType, |
| 176 | + GraphQLString, |
| 177 | + GraphQLNonNull, |
| 178 | + GraphQLSchema, |
| 179 | +} from 'graphql'; |
| 180 | + |
| 181 | +const ProductType = new GraphQLObjectType({ |
| 182 | + name: 'Product', |
| 183 | + fields: { |
| 184 | + name: { type: new GraphQLNonNull(GraphQLString) }, |
| 185 | + }, |
| 186 | +}); |
| 187 | + |
| 188 | +const QueryType = new GraphQLObjectType({ |
| 189 | + name: 'Query', |
| 190 | + fields: { |
| 191 | + product: { |
| 192 | + type: ProductType, |
| 193 | + resolve: () => ({ name: null }), |
| 194 | + }, |
| 195 | + }, |
| 196 | +}); |
| 197 | + |
| 198 | +const schema = new GraphQLSchema({ query: QueryType }); |
| 199 | +``` |
| 200 | + |
| 201 | +In this example, the `product` resolver returns an object with `name: null`. |
| 202 | +Because the `name` field is non-nullable, GraphQL.js responds by |
| 203 | +nullifying the entire `product` field and appending a |
| 204 | +corresponding error to the response. |
| 205 | + |
| 206 | +## Best practices |
| 207 | + |
| 208 | +- Default to nullable. Start with nullable fields and introduce non-null |
| 209 | +constraints when data consistency is guaranteed. |
| 210 | +- Express intent. Use non-null when a field must always be present for logical |
| 211 | +correctness. |
| 212 | +- Validate early. Add checks in resolvers to prevent returning `null` for |
| 213 | +non-null fields. |
| 214 | +- Watch for nesting. Distinguish between: |
| 215 | + - `[User]!` - nullable list of non-null users |
| 216 | + - `[User!]!` - non-null list of non-null users |
| 217 | + |
| 218 | +## Additional resources |
| 219 | + |
| 220 | +- [GraphQL Specification: Non-null](https://spec.graphql.org/draft/#sec-Non-Null): |
| 221 | +Defines the formal behavior of non-null types in the GraphQL type system and |
| 222 | +execution engine. |
| 223 | +- [Understanding GraphQL.js Errors](website\pages\docs\graphql-errors.mdx): Explains |
| 224 | +how GraphQL.js propagates and formats execution-time errors. |
| 225 | +- [Anatomy of a Resolver](website\pages\docs\resolver-anatomy.mdx): Breaks down |
| 226 | +how resolvers work in GraphQL.js. |
| 227 | +- [Constructing Types](website\pages\docs\constructing-types.mdx): Shows how |
| 228 | +to define and compose types in GraphQL.js. |
0 commit comments