🎬 That's a Wrap for GraphQLConf 2024! • Watch the Videos • Check out the recorded talks and workshops
DocumentationNullability in GraphQL.js

Nullability in GraphQL.js

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.

How nullability works

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 as null bubbling.

Understanding nullability requires familiarity with the GraphQL type system, execution semantics, and the trade-offs involved in schema design.

The role of GraphQLNonNull

GraphQL.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:

  • Field types
  • Argument types
  • Input object fields
  • Return types for resolvers

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.

Execution behavior

GraphQL.js uses nullability rules to determine how to handle null values at runtime:

  • If a nullable field returns null, the result includes that field with a null value.
  • If a non-nullable field returns 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.

Schema design considerations

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:

  • Use non-null types when a value is always expected. This reflects intent and reduces ambiguity for clients.
  • Avoid aggressive use of non-null types in early schema versions. It limits your ability to evolve the API later.
  • Be cautious of error bubbling. A null return from a deeply nested non-nullable field can affect large portions of the response.

Versioning

Non-null constraints are part of a field’s contract:

  • Changing a field from non-nullable to nullable is a breaking change.
  • Changing from nullable to non-nullable is also breaking unless you can guarantee that the field will never return null.

To reduce the risk of versioning issues, start with nullable fields and add constraints as your schema matures.

Using GraphQLNonNull in schema and resolvers

Let’s walk through two practical scenarios that show how GraphQL.js enforces nullability.

Defining a non-null field

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.

Resolver returns null for a non-null field

In 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.

Best practices

  • Default to nullable. Start with nullable fields and introduce non-null constraints when data consistency is guaranteed.
  • Express intent. Use non-null when a field must always be present for logical correctness.
  • Validate early. Add checks in resolvers to prevent returning null for non-null fields.
  • Watch for nesting. Distinguish between:
    • [User]! - nullable list of non-null users
    • [User!]! - non-null list of non-null users

Additional resources