GraphQL Schema Design
Expert guidance for designing well-structured GraphQL schemas.
Quick Reference
| Concept |
Best Practice |
Example |
| Nullability |
Default nullable, required only when necessary |
email: String not email: String! |
| Pagination |
Relay connections (edges/nodes) |
users(first: Int, after: String): UserConnection! |
| Naming |
PascalCase types, camelCase fields |
type UserProfile { firstName: String } |
| Descriptions |
All types, fields, arguments |
"""User account""" |
| Mutations |
Noun + Verb pattern |
createUser, deletePost |
| Deprecations |
@deprecated with reason |
@deprecated(reason: "Use newField") |
What Do You Need?
- Type design - Structs, interfaces, unions, enums
- Field design - Nullability, arguments, defaults
- Pagination - Relay-style connections
- Naming - Conventions for consistency
- Documentation - Descriptions, deprecations
Specify a number or describe your schema concern.
Routing
| Response |
Reference to Read |
| 1, "type", "interface", "union", "enum" |
types.md |
| 2, "field", "argument", "nullable", "default" |
fields.md |
| 3, "pagination", "connection", "relay" |
pagination.md |
| 4, "naming", "convention", "consistency" |
naming.md |
| 5, "description", "deprecation", "document" |
documentation.md |
Critical Rules
- Default to nullable: Easier to make required later than vice versa
- Use Relay pagination: Connections with edges/nodes, not lists
- Document everything: Schema is the API documentation
- Deprecate before removing: @deprecated with reason, wait for clients to migrate
- Noun mutations for state changes: createUser, deletePost, closeCard
- Avoid business logic in schema: Schema describes shape, not behavior
Schema Template
"""
A user in the system
"""
type User {
"""
The unique identifier of the user
"""
id: ID!
"""
The user's display name
"""
name: String!
"""
The user's email address (optional if not public)
"""
email: String
"""
Posts created by this user, paginated
"""
posts(
"""
Number of posts to return
"""
first: Int
"""
Cursor for pagination
"""
after: String
): PostConnection!
}
"""
Paginated connection of posts
"""
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type PostEdge {
node: Post!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
Common Schema Issues
| Issue |
Severity |
Fix |
| Unbounded lists |
High |
Use pagination connections |
| Missing descriptions |
Medium |
Add doc comments |
| Inconsistent nullability |
Medium |
Be intentional about ! |
| Breaking changes without deprecation |
High |
Use @deprecated first |
| CRUD-style mutations |
Low |
Use noun+verb (createUser) |
| No pagination on collections |
High |
Add Relay connections |
Nullability Guidelines
# Good: Nullable by default
type User {
id: ID!
name: String! # Required for user
email: String # Optional (not all users have email)
bio: String # Optional (not all users filled it out)
posts(first: Int): PostConnection! # Connection required, edges may be empty
}
# Avoid: Too many required fields
type User {
id: ID!
name: String!
email: String! # Required may block mutations
phone: String! # Required may block mutations
bio: String! # Required may block mutations
}
Reference Index
| File |
Topics |
| types.md |
Objects, interfaces, unions, enums, scalars |
| fields.md |
Nullability, arguments, defaults, lists |
| pagination.md |
Relay connections, edges, nodes, cursors |
| naming.md |
Conventions for types, fields, mutations |
| documentation.md |
Descriptions, deprecations, comments |
Success Criteria
Schema is well-designed when:
- All types and fields have descriptions
- Collections use Relay pagination (not unbounded lists)
- Nullability is intentional (not default required)
- Naming follows conventions (PascalCase, camelCase)
- Deprecated fields have @deprecated with reason
- No breaking changes without deprecation period
- Schema reads as documentation for clients
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: graphql-schema-design-23description: GraphQL schema design including types, fields, pagination, nullability, naming conventions, and descriptions. Use when designing or modifying GraphQL schemas. Use when this capability is needed.4---56# GraphQL Schema Design78Expert guidance for designing well-structured GraphQL schemas.910## Quick Reference1112| Concept | Best Practice | Example |13|---------|---------------|---------|14| Nullability | Default nullable, required only when necessary | `email: String` not `email: String!` |15| Pagination | Relay connections (edges/nodes) | `users(first: Int, after: String): UserConnection!` |16| Naming | PascalCase types, camelCase fields | `type UserProfile { firstName: String }` |17| Descriptions | All types, fields, arguments | `"""User account"""` |18| Mutations | Noun + Verb pattern | `createUser`, `deletePost` |19| Deprecations | @deprecated with reason | `@deprecated(reason: "Use newField")` |2021## What Do You Need?22231. **Type design** - Structs, interfaces, unions, enums242. **Field design** - Nullability, arguments, defaults253. **Pagination** - Relay-style connections264. **Naming** - Conventions for consistency275. **Documentation** - Descriptions, deprecations2829Specify a number or describe your schema concern.3031## Routing3233| Response | Reference to Read |34|----------|-------------------|35| 1, "type", "interface", "union", "enum" | [types.md](./references/types.md) |36| 2, "field", "argument", "nullable", "default" | [fields.md](./references/fields.md) |37| 3, "pagination", "connection", "relay" | [pagination.md](./references/pagination.md) |38| 4, "naming", "convention", "consistency" | [naming.md](./references/naming.md) |39| 5, "description", "deprecation", "document" | [documentation.md](./references/documentation.md) |4041## Critical Rules4243- **Default to nullable**: Easier to make required later than vice versa44- **Use Relay pagination**: Connections with edges/nodes, not lists45- **Document everything**: Schema is the API documentation46- **Deprecate before removing**: @deprecated with reason, wait for clients to migrate47- **Noun mutations for state changes**: createUser, deletePost, closeCard48- **Avoid business logic in schema**: Schema describes shape, not behavior4950## Schema Template5152```graphql53"""54A user in the system55"""56type User {57 """58 The unique identifier of the user59 """60 id: ID!6162 """63 The user's display name64 """65 name: String!6667 """68 The user's email address (optional if not public)69 """70 email: String7172 """73 Posts created by this user, paginated74 """75 posts(76 """77 Number of posts to return78 """79 first: Int80 """81 Cursor for pagination82 """83 after: String84 ): PostConnection!85}8687"""88Paginated connection of posts89"""90type PostConnection {91 edges: [PostEdge!]!92 pageInfo: PageInfo!93 totalCount: Int!94}9596type PostEdge {97 node: Post!98 cursor: String!99}100101type PageInfo {102 hasNextPage: Boolean!103 hasPreviousPage: Boolean!104 startCursor: String105 endCursor: String106}107```108109## Common Schema Issues110111| Issue | Severity | Fix |112|-------|----------|-----|113| Unbounded lists | High | Use pagination connections |114| Missing descriptions | Medium | Add doc comments |115| Inconsistent nullability | Medium | Be intentional about ! |116| Breaking changes without deprecation | High | Use @deprecated first |117| CRUD-style mutations | Low | Use noun+verb (createUser) |118| No pagination on collections | High | Add Relay connections |119120## Nullability Guidelines121122```graphql123# Good: Nullable by default124type User {125 id: ID!126 name: String! # Required for user127 email: String # Optional (not all users have email)128 bio: String # Optional (not all users filled it out)129 posts(first: Int): PostConnection! # Connection required, edges may be empty130}131132# Avoid: Too many required fields133type User {134 id: ID!135 name: String!136 email: String! # Required may block mutations137 phone: String! # Required may block mutations138 bio: String! # Required may block mutations139}140```141142## Reference Index143144| File | Topics |145|------|--------|146| [types.md](./references/types.md) | Objects, interfaces, unions, enums, scalars |147| [fields.md](./references/fields.md) | Nullability, arguments, defaults, lists |148| [pagination.md](./references/pagination.md) | Relay connections, edges, nodes, cursors |149| [naming.md](./references/naming.md) | Conventions for types, fields, mutations |150| [documentation.md](./references/documentation.md) | Descriptions, deprecations, comments |151152## Success Criteria153154Schema is well-designed when:155- All types and fields have descriptions156- Collections use Relay pagination (not unbounded lists)157- Nullability is intentional (not default required)158- Naming follows conventions (PascalCase, camelCase)159- Deprecated fields have @deprecated with reason160- No breaking changes without deprecation period161- Schema reads as documentation for clients162163---164> Converted and distributed by [TomeVault](https://tomevault.io/claim/jovermier) — claim your Tome and manage your conversions.165<!-- tomevault:4.0:skill_md:2026-04-13 -->