10 Principles for Designing Good GraphQL Schemas - WunderGraph
CEO & Co-Founder at WunderGraph
January 12, 2026·25min read
Edited on July 9, 2026 by Brendan Bondurant
TL;DR
A good GraphQL schema lets clients fetch what they need in as few requests as possible, reads clearly, and evolves without breaking consumers. This guide covers 10 principles for getting there: design around client capabilities rather than your database, model expected errors as union types, be deliberate about nullability and its blast radius, paginate every list, abstract away implementation details, and assume you can never truly deprecate a published field. The last principle is organizational: match your schema to your team structure, which for multiple teams usually means federation with one subgraph per team.
What is a (good) GraphQL Schema?
First, let's define what a (good) GraphQL schema is. Compared to REST APIs, a schema is never optional in a GraphQL API. A REST API might be documented in an OpenAPI specification, but that is not a hard requirement.
A GraphQL server, if it follows the GraphQL specification, must always implement certain root fields that allow clients to introspect the schema. This is powerful because it allows tools like GraphiQL and GraphQL Playground to introspect the schema and leverage it for various purposes.
While GraphiQL shows an interactive interface to explore the schema, CLI tools like GraphQL Codegen can generate typesafe client code simply by pointing at the schema.
In a nutshell, the GraphQL schema is the definition of the API contract. It's all the types and fields that are available for the clients to "query".
10 Principles for Designing Good GraphQL Schemas
Here are 10 GraphQL schema design principles we see working across companies like eBay, SoundCloud, Paramount, and Shutterstock:
| Principle | Why it matters |
|---|---|
| Capability-based design | Aligns schema with real business workflows |
| Client-centric design | Minimizes round-trips and awkward client code |
| It's ok to repeat yourself | Clarifies intent and access rules |
| Make expected errors explicit | Enables type-safe client UX for unhappy paths |
| Nullability & absence of data | Handles failures without confusing nulls |
| Null blast radius | Prevents one error from nulling your whole query |
| Assume you can never deprecate | Encourages durable contracts and careful changes |
| Pagination is a must | Critical for UX, security, and rate limiting |
| Abstract implementation details | Lets internals change without breaking clients |
| Match your org structure | Uses Conway's Law to pick monolith vs federation |
1. Capability-based design
Before designing a schema, ask yourself what capabilities you need to support. What are the jobs to be done that you need to support? What workflows are clients trying to achieve? What user experiences are clients trying to achieve?
2. Client-centric design
It's important to center the design around the clients and their usage patterns and requirements, contrasting to a datasource-centric design that is driven by the underlying data models or REST APIs.
3. It's ok to repeat yourself
Focusing on capabilities and client-centric design allows us to find clear boundaries between different subdomains, making it easier for us to understand the difference between concepts in our schema.
4. Make expected errors explicit
Use the type system for known outcomes instead of using generic errors for expected outcomes.
5. Nullability and absence of data
GraphQL should distinguish between absence of data, a null value, and a null as an error consequence.
6. Null blast radius
Minimize the blast radius of non-nullable fields, ensuring that marking them as non-nullable is done only when certain that the data will always be present.
7. Assume that you can never deprecate a field
Consider the implications of releasing an API and the importance of its longevity, especially when multiple consumers are involved.
8. Pagination is a must
Paginated results are essential for user experience and environmental security, preventing issues that arise from unbounded lists.
9. Abstract away implementation details
Design your schema around client use cases to keep internal changes from disrupting your contract with clients.
10. The Schema should match your organizational structure
Design schemas that correspond to your organization, to follow Conway's Law for better alignment of development and usage.
Conclusion
Designing a good GraphQL schema is both an art and a discipline. The principles we've covered provide a framework for thoughtful decisions that will serve your clients well over time.
Remember that schema design is iterative. Start with a solid foundation based on these principles, gather feedback from your clients, and evolve your schema thoughtfully.
If you believe good schemas require collaboration, explore WunderGraph Hub, a collaborative canvas for schema development.