Objects, Lists and Non-Null
A book has one author and many reviews. Some books have no reviews yet; a book without an author should never exist. A schema captures all of that in a few characters, and reading those characters correctly tells you exactly what null the client might ever see.
The rule
!means never null; read list types from the outside in.
An object type is a named set of fields, each with a type and optionally its own arguments:
type Book {
id: ID!
title: String!
genre: Genre!
author: Author!
reviews(first: Int = 5): [Review!]!
subtitle: String
}subtitle: Stringmay benull(many books have none).author: Author!is nevernull.reviews(first: Int = 5)is a field argument with a default;reviews { rating }returns up to five.
For lists, the two positions of ! mean different things. Read from the outside in: first "can the list be null?", then "can an item be null?"
| Type | List null? | Item null? | Allowed values |
|---|---|---|---|
[Review] |
yes | yes | null, [], [r1, null] |
[Review]! |
no | yes | [], [r1, null] |
[Review!] |
yes | no | null, [], [r1] |
[Review!]! |
no | no | [], [r1, r2] |
[Review!]! is the usual choice for "zero or more reviews": the client can loop without null checks. An empty list is still allowed.
A ! is a promise, and if a resolver breaks it, GraphQL does not send the forbidden null. Instead, the null moves up to the nearest parent that is allowed to be null, and an error explains why. If Book.author fails for b1, then book itself becomes null, because Query.book returns a nullable Book. Mark fields non-null when they truly always exist; overusing ! lets one failure blank out large parts of a response.
One convenience on input: if an argument expects a list, such as ids: [ID!]!, and you pass a single value "b1", it is treated as ["b1"].
The shortcut
To read any field type, say it aloud from the outside in: "a non-null list of non-null reviews" for [Review!]!. Then list which values that sentence allows.
The query
{ book(id: "b1") { title author { name } } }
Drills
Answer fast — trust the rule.
Which type means "always a list, possibly empty, with no null items"?
Which value is allowed for a field of type [Review]!?
Book.author is Author! and fails for b1. Query.book returns Book. What is data.book?
With reviews(first: Int = 5): [Review!]!, how many reviews can book(id: "b1") { reviews { rating } } return at most?
Remember
!means never null; read list types from the outside in.- Say the type aloud ("a non-null list of non-null reviews"), then list the allowed values.
- Trap: a broken
!promise nulls the nearest nullable parent, so do not overuse!.
