Skip to main content
GraphQL

GraphQL schema SDL: how to read the contract before you write resolvers

Nullability, lists, inputs, and deprecations are API commitments—read the punctuation before implementing a resolver.

Thien Nguyen
By Thien Nguyen
Updated July 21, 2026 · 1 min read

GraphQL SDL is compact enough to hide a serious contract: a misplaced ! changes how failures propagate to clients. Treat schema punctuation as product behaviour, not resolver scaffolding.

type Query { order(id: ID!): Order }
type Order { id: ID!, totalCents: Int!, notes: String }
TypeContract
StringValue can be null
String!Resolver must supply a value
[User]List and entries can be null
[User!]!List exists and every entry exists

Non-null failures travel upward

If totalCents resolves null, GraphQL nulls Order until it reaches a nullable parent. Make a field non-null only when the service can genuinely uphold that promise across old data and partial outages.

A JSON scalar is not a shortcut around schema design. It removes validation, discoverability, and typed client generation for data that probably has a stable shape.

Evolve the contract deliberately

Add fields, deprecate old ones with a reason, observe client usage, then remove on a published timeline. For expected business failures, model a union such as CreateOrderResult = Order | ValidationError; do not force clients to infer intent from a generic server error. Cursor connections deserve an explicit type too—large lists should not quietly become unbounded response bodies.

The schema is the public API. Resolvers are its implementation detail.

References

Primary documentation and specifications checked when this article was last updated.

GraphQLAPIsSchema Design

Related articles

All articles