GraphQL in Production · GraphQL Fundamentals for API Designers

Designing a GraphQL Schema

Types, nullability, IDs, naming and modeling the domain for clients.

Design the schema around client use cases and domain language, not database tables. Use globally unique IDs (ID!, often base64 Type:id or prefixed IDs) to enable refetching (node(id:)) and client caches. Be deliberate with nullability: non-null (!) is a promise — if a non-null field fails, the null bubbles up to the nearest nullable parent. Prefer nullable fields for data from unreliable dependencies, non-null for guaranteed data. Use enums for closed sets, custom scalars (DateTime, Money) for formats, and descriptions everywhere (they become docs).

Nullability guidance

Choice Use non-null (!) when Use nullable when
Scalar fields Always present in your own DB Optional data or computed from other services
Object references Same service, guaranteed Cross-service / may fail / permission-dependent
Lists [Item!]! — list and items always exist Items may be individually unresolvable
Arguments Required inputs Optional filters with defaults
"""A customer order."""
type Order implements Node {
  id: ID!
  number: String!
  status: OrderStatus!
  total: Money!
  createdAt: DateTime!
  customer: Customer            # nullable: customer service may be unavailable
  items(first: Int = 20, after: String): OrderItemConnection!
}

enum OrderStatus { PENDING PAID SHIPPED CANCELLED }

type Money { amount: Int!  currency: String! }   # minor units

interface Node { id: ID! }
type Query { node(id: ID!): Node  order(id: ID!): Order  viewer: User }

Common pitfalls

  • Everything non-null: one failing resolver nulls out the whole response.
  • Exposing database IDs and join tables directly.
  • Generic JSON scalars that defeat typing.

Key points

  • Model for clients, not tables
  • Global IDs + Node interface
  • Nullability is a contract

Further reading: GraphQL: Schemas and types

Practice questions

What happens when a non-null field's resolver throws?

Answer

The field can't be null, so null propagates to the nearest nullable parent field (possibly the entire data), and an error is added to errors.

Why do client caches like Apollo and Relay want globally unique IDs?

Answer

They normalize objects by type+id so the same entity fetched by different queries is stored once and updates propagate everywhere.

Open in the app to mark lessons complete, take notes and track your progress.