Working with Lists and Non-Null

1. Defining List Types

NotationMeaning
[Type]Nullable list of nullable items
[Type!]Nullable list, items required
[Type]!Required list, items nullable
[Type!]!Required list, items required
[[Type!]!]!Nested list (matrix)

2. Making Lists Required

FormResolver Behavior
[T]!Resolver must return an array (may contain nulls)
[T]May return null OR array

3. Making List Items Required

FormResolver Behavior
[T!]Items resolve null → error propagates and entire list becomes null
[T]Null items kept as nulls in the array

4. Combining Modifiers

RecommendedWhy
[T!]!Most restrictive; clearest contract
[T!]List itself optional (no permission, etc.)
[T]Avoid — ambiguous nulls everywhere

5. Understanding Nullability Rules

Resolver returns null on Non-Null field
       │
       ▼
Field result becomes error
       │
       ▼
Propagates UP to nearest nullable ancestor
       │
       ▼
That ancestor becomes null; siblings preserved
      

6. Returning Empty Lists

ReturnSemantics
[]"No items found"
null"List unavailable / not applicable" (only on nullable list)
Note: Prefer empty array over null for collections — clients can iterate without null checks.

7. Handling Null Items

ScenarioPattern
Sparse dataUse [T] to allow nulls
Filtered resultsSkip null in resolver, return [T!]!
AuthorizationReplace with placeholder type or filter out

8. Paginating Lists

StrategyArgs
Offsetlimit, offset
Cursor (Relay)first, after, last, before
Page-basedpage, perPage

9. Limiting List Size

MechanismDetail
Max firstCap arg in resolver: Math.min(args.first, 100)
@cost directiveCost = first × childCost
Validation ruleReject queries exceeding total cost

10. Sorting List Results

Example: Multi-field sort

enum SortDirection { ASC DESC }
input PostOrderBy { field: PostSortField!, direction: SortDirection! = DESC }

type Query {
  posts(orderBy: [PostOrderBy!] = [{ field: CREATED_AT, direction: DESC }]): [Post!]!
}