A GraphQL schema defines available operations, field types, arguments, and nullability. A non-null marker promises that a successful execution path will provide a value; if a resolver cannot do so, error propagation may null a larger parent selection. That can surprise a client that expected only one optional field to fail. Schema evolution should be additive where possible, with deprecation and usage evidence before removal. Types express shape, not access rights or runtime cost. A field appearing in introspection does not mean every caller may read every object returned by that field.
GraphQL Schema Nullability and Evolution
Working case
The case-review API exposes Case with id, status, owner, and an optional inspectionDueAt. The team wants to add riskBand. Early cases have no score, so declaring riskBand non-null would let an old record’s missing score null its containing Case selection. The team adds a nullable field, documents when it is absent, and updates only clients that need it. Months later, ownerName is replaced by an Owner object. The old field remains during a compatibility window while usage telemetry identifies clients still requesting it. Both fields use the same current tenant and case authorization rule.
Implementation boundary
function riskBandForCase(caseRecord) {
return caseRecord.score == null ? null : caseRecord.score >= 47 ? "high" : "normal";
}
console.log(riskBandForCase({ score: null }));
// Output: nullName fields after stable domain concepts instead of current database columns. Choose nullability by actual data and failure behavior, including old records and permission-filtered relations. Distinguish an empty list from an unavailable list and from a null parent. Add fields before removing old ones; deprecate with a reason and record usage by operation, not private query variables. Keep schema changes and resolver behavior synchronized across deployments. Validate generated client types against the deployed schema and test old operation documents during rollout. Authorization belongs in data access or resolver policy, not in whether a field is visible in schema text. Limit introspection as appropriate to the service, but never treat hiding it as security.
Cost and boundaries
Schema validation is bounded by the requested document size and type references; each added field is cheap to define but can add resolver and compatibility work. Keeping deprecated fields consumes code and test time until clients migrate. A non-null promise can simplify clients but raises failure blast radius when a downstream dependency is unavailable. Measure field use, null responses by cause, client operation versions, and error paths before tightening a field contract. Avoid moving a field from nullable to non-null merely because current fixtures happen to contain values.
Failure trace
The team changes owner from nullable to non-null because new cases always have an owner. Historical unassigned cases still exist; their owner resolver returns null, and an otherwise useful case list becomes null for some clients. Preserve the honest nullable contract or migrate all data and failure paths first. Another change removes ownerName after only the newest web bundle migrates, breaking mobile or cached clients. Test old operations, empty and historical records, downstream timeout, permission changes, deprecated field use, and a partial error path.
Verification
- Historical and failure cases satisfy declared nullability.
- Old operation documents work during the migration window.
- Schema visibility never substitutes for object authorization.
Practice drill
Draft Case fields for id, status, owner, inspectionDueAt, and riskBand using the actual old-data states. Query a historical unassigned case, a current assigned case, and a downstream owner lookup failure. Record how each null propagates under chosen declarations. Add Owner while retaining ownerName and run an older operation document against the new schema. Deny one case to reviewer 62 and confirm both old and new owner fields obey the same object authorization.
Decision note
Make nullability match real data and failure paths, then evolve fields with observed client compatibility.
Common Mistakes
- Marking a field non-null based only on new records.
- Removing a field before old clients stop requesting it.
- Treating a typed schema as a permission system.
Connected lessons
GraphQL Query and Resolver Boundaries; GraphQL Resolver Batching and Tenant Scope; GraphQL Operation Cost and Admission; GraphQL Mutation Errors and Pagination Contracts; API Evolution and Compatibility Windows; Object-Level Authorization for Reads and Writes; Tests across boundaries: assert behavior, not implementation text.
Apply and check
Build Project: tenant-safe GraphQL case API and review Web Development: release and GraphQL decisions quiz.
