100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

How Do You Design a Good GraphQL Schema?

Learn GraphQL schema design best practices: nullability, structured input types, additive evolution, and modeling relationships.

hardQ101 of 224 in Web Development Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

A good GraphQL schema models the domain as a graph of types connected by meaningful relationships, favors nullable fields and explicit input types over overloaded parameters, and is designed around what clients need to query rather than mirroring the database structure one-to-one.

Schema design starts with types representing real entities and their relationships as fields, so a Query field like “user(id: ID!): User” naturally lets clients traverse into “user.posts” or “user.posts.comments” without new endpoints. Fields should be nullable by default unless a value is truly guaranteed, because a single failing nested resolver should not null out an entire response tree; non-null fields propagate errors upward and can wipe out unrelated data. Mutations should accept a single structured “input” type rather than many loose scalar arguments, both for readability and because input types can evolve by adding optional fields without breaking existing clients. Naming and versioning follow additive evolution: new fields get added, old fields get deprecated with the "@deprecated" directive and a reason, and breaking changes are avoided rather than expressed through a new endpoint version, since the schema itself is the contract.

  • Nullable fields prevent one failing nested resolver from nulling the whole response
  • Structured input types allow additive changes without breaking existing clients
  • @deprecated directive lets fields evolve without hard version breaks
  • Modeling relationships as graph edges avoids proliferating bespoke endpoints

AI Mentor Explanation

Designing a GraphQL schema is like designing a scorecard system where a team links to its players, and each player links to their innings stats, letting anyone traverse from team to player to performance without separate lookup sheets for each combination. If a schema instead demanded rigid non-null fields everywhere, one missing stat for an injured player could blank out the entire scorecard instead of just that one field. A well-designed system lets you add a new stat column next season without breaking the sheets that already exist. That graph-of-relationships, additive-evolution approach is exactly what good schema design achieves.

Step-by-Step Explanation

  1. Step 1

    Model entities and relationships

    Define types around real domain entities, connecting them via fields rather than mirroring database tables.

  2. Step 2

    Decide nullability deliberately

    Default to nullable fields so a failing nested resolver does not null out the whole response tree.

  3. Step 3

    Use structured input types

    Mutations take a single input object so new optional fields can be added without breaking clients.

  4. Step 4

    Plan additive evolution

    Deprecate fields with @deprecated and a reason instead of introducing breaking version bumps.

What Interviewer Expects

  • Understanding of why fields should be nullable by default
  • Awareness of input types for mutations and their evolvability
  • Knowledge of schema versioning via deprecation rather than breaking changes
  • Modeling the domain as a graph rather than a direct database mirror

Common Mistakes

  • Making every field non-null “for safety,” which causes cascading null propagation
  • Passing many loose scalar arguments to mutations instead of one input type
  • Designing the schema as a 1:1 mirror of database tables
  • Introducing a v2 endpoint instead of deprecating fields additively

Best Answer (HR Friendly)

A good GraphQL schema mirrors how the data actually relates in the real world, so a client can navigate from one thing to a related thing in a single request. I keep fields optional so one small missing piece of data does not break the whole response, and I design mutations with a single structured input so I can add new options later without breaking apps already using the API.

Code Example

Nullable relationships and a structured mutation input
type User {
  id: ID!
  name: String!
  posts: [Post!]!
  bio: String
}

type Post {
  id: ID!
  title: String!
  author: User
  publishedAt: String
}

input CreatePostInput {
  title: String!
  body: String!
  tags: [String!]
}

type Mutation {
  createPost(input: CreatePostInput!): Post!
}

Follow-up Questions

  • When should a field be non-null versus nullable in GraphQL?
  • How do you handle pagination in a GraphQL schema design?
  • What is the @deprecated directive and how should it be used?
  • How would you design a schema to avoid the N+1 resolver problem from the start?

MCQ Practice

1. Why default to nullable fields in a GraphQL schema?

Non-null errors propagate upward and can null out an entire branch of the response tree.

2. Why use a single input type for mutation arguments?

Structured input types can grow additively, which loose scalar arguments cannot do as cleanly.

3. How should a GraphQL schema handle breaking changes?

GraphQL favors additive, deprecation-based evolution over versioned endpoints.

Flash Cards

Default nullability rule?Fields should be nullable by default to avoid cascading null propagation.

How should mutations take arguments?Via a single structured input type, not many loose scalars.

How does GraphQL handle breaking changes?Additive evolution with @deprecated fields instead of new versioned endpoints.

Should a schema mirror the database?No — it should model the domain graph and relationships clients actually need.

1 / 4

Continue Learning