website/pages/docs/nullability.mdx
Nullability is a core concept in GraphQL that affects how schemas are defined, how execution behaves, and how clients interpret results. In GraphQL.js, nullability plays a critical role in both schema construction and runtime behavior.
This guide explains how nullability works, how it's represented in GraphQL.js, and how to design schemas with nullability in mind.
In GraphQL, fields are nullable by default. This means if a resolver function
returns null, the result will include a null value unless the field is
explicitly marked as non-nullable.
When a non-nullable field resolves to null, the GraphQL execution engine
raises a runtime error and attempts to recover by replacing the nearest
nullable parent field with null. This behavior is known formally as "error
propagation" but more commonly as null bubbling.
Understanding nullability requires familiarity with the GraphQL type system, execution semantics, and the trade-offs involved in schema design.
GraphQLNonNullGraphQL.js represents non-nullability using the GraphQLNonNull wrapper type.
By default, all fields are nullable:
import {
GraphQLObjectType,
GraphQLString,
GraphQLNonNull,
} from 'graphql';
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: new GraphQLNonNull(GraphQLString) },
email: { type: GraphQLString },
}),
});
In this example, the id field is non-nullable, meaning it must always
resolve to a string. The email field is nullable.
You can use GraphQLNonNull with:
You can also combine it with other types to create more specific constraints. For example:
new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(UserType)))
This structure corresponds to [User!]! in SDL: a non-nullable list of non-null
User values. When reading code like this, work from the inside out: UserType
is non-nullable, and wrapped in a list, which is itself non-nullable.
GraphQL.js uses nullability rules to determine how to handle null values
at runtime:
null, the result includes that field with
a null value.null, GraphQL throws an error and
sets the nearest nullable parent field to null.This bubbling behavior prevents partial data from being returned in cases where a non-nullable guarantee is violated.
Here's an example that shows this in action:
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLNonNull,
} from 'graphql';
const UserType = new GraphQLObjectType({
name: 'User',
fields: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
});
const QueryType = new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
resolve: () => ({ id: null }),
},
},
});
const schema = new GraphQLSchema({ query: QueryType });
In this example, the user field returns an object with id: null.
Because id is non-nullable, GraphQL can't return user.id, and instead
nullifies the user field entirely. An error describing the violation is
added to the errors array in the response.
Using non-null types communicates clear expectations to clients, but it's
also less forgiving. When deciding whether to use GraphQLNonNull, keep
the following in mind:
null return from a deeply nested non-nullable
field can affect large portions of the response.Non-null constraints are part of a field's contract:
null values which they do not have
handling code for.To reduce the risk of versioning issues, start with nullable fields and add constraints as your schema matures.
GraphQLNonNull in schema and resolversLet's walk through two practical scenarios that show how GraphQL.js enforces nullability.
This example defines a Product type with a non-nullable name field:
import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from 'graphql';
const ProductType = new GraphQLObjectType({
name: 'Product',
fields: () => ({
name: { type: new GraphQLNonNull(GraphQLString) },
}),
});
This configuration guarantees that name must always be a string
and never null. If a resolver returns null for this field, an
error will be thrown.
null for a non-null fieldIn this example, the resolver returns an object with name: null, violating
the non-null constraint:
import {
GraphQLObjectType,
GraphQLString,
GraphQLNonNull,
GraphQLSchema,
} from 'graphql';
const ProductType = new GraphQLObjectType({
name: 'Product',
fields: {
name: { type: new GraphQLNonNull(GraphQLString) },
},
});
const QueryType = new GraphQLObjectType({
name: 'Query',
fields: {
product: {
type: ProductType,
resolve: () => ({ name: null }),
},
},
});
const schema = new GraphQLSchema({ query: QueryType });
In this example, the product resolver returns an object with name: null.
Because the name field is non-nullable, GraphQL.js responds by
nullifying the entire product field and appending a
corresponding error to the response.
null for
non-null fields.[User!] - nullable list of non-null users[User]! - non-null list of nullable users[User!]! - non-null list of non-null users