
graphql-schema-design
Design production-grade GraphQL schemas with best practices and patterns
1estrelas
0forks
Atualizado 1/5/2026
SKILL.md
readonlyread-only
name
graphql-schema-design
description
Design production-grade GraphQL schemas with best practices and patterns
version
"2.0.0"
GraphQL Schema Design Skill
Architect scalable, maintainable GraphQL APIs
Overview
Learn industry-standard patterns for designing GraphQL schemas that scale. Covers naming conventions, pagination, error handling, and schema evolution.
Quick Reference
| Pattern | When to Use | Example |
|---|---|---|
| Connection | Paginated lists | users: UserConnection! |
| Payload | Mutation results | CreateUserPayload |
| Input | Mutation args | CreateUserInput |
| Interface | Shared fields | interface Node { id: ID! } |
| Union | Multiple types | SearchResult = User | Post |
Core Patterns
1. Naming Conventions
# Types: PascalCase
type User { }
type UserProfile { }
# Fields: camelCase
type User {
firstName: String!
lastName: String!
createdAt: DateTime!
isActive: Boolean! # Boolean prefix: is, has, can
}
# Queries: noun (singular/plural)
type Query {
user(id: ID!): User # Singular
users: UserConnection! # Plural
}
# Mutations: verb + noun
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
# Actions
sendEmail(input: SendEmailInput!): SendEmailPayload!
publishPost(id: ID!): PublishPostPayload!
}
# Inputs: [Action][Type]Input
input CreateUserInput { }
input UpdateUserInput { }
input UserFilterInput { }
# Payloads: [Action][Type]Payload
type CreateUserPayload { }
type UpdateUserPayload { }
2. Relay-Style Pagination
# Connection pattern
type Query {
users(
first: Int
after: String
last: Int
before: String
filter: UserFilter
): UserConnection!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# Usage
query GetUsers {
users(first: 10, after: "cursor123") {
edges {
node { id name }
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
3. Error Handling
# Payload pattern (recommended)
type CreateUserPayload {
user: User
errors: [UserError!]!
}
type UserError {
field: String
message: String!
code: UserErrorCode!
}
enum UserErrorCode {
INVALID_EMAIL
DUPLICATE_EMAIL
WEAK_PASSWORD
NOT_FOUND
UNAUTHORIZED
}
# Union pattern (type-safe)
union CreateUserResult =
| CreateUserSuccess
| ValidationError
| NotAuthorizedError
type CreateUserSuccess {
user: User!
}
type ValidationError {
field: String!
message: String!
}
type NotAuthorizedError {
message: String!
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserResult!
}
4. Node Interface
# Global object identification
interface Node {
id: ID!
}
type Query {
node(id: ID!): Node
nodes(ids: [ID!]!): [Node]!
}
type User implements Node {
id: ID!
name: String!
}
type Post implements Node {
id: ID!
title: String!
}
# Enables refetching any object by ID
query RefetchUser {
node(id: "User:123") {
... on User {
name
email
}
}
}
5. Schema Organization
# schema.graphql (root)
type Query {
# User domain
user(id: ID!): User
users(filter: UserFilter): UserConnection!
# Product domain
product(id: ID!): Product
products(filter: ProductFilter): ProductConnection!
}
type Mutation {
# User mutations
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
# Product mutations
createProduct(input: CreateProductInput!): CreateProductPayload!
}
# types/user.graphql
type User implements Node {
id: ID!
email: String!
name: String!
createdAt: DateTime!
orders: OrderConnection!
}
# types/product.graphql
type Product implements Node {
id: ID!
name: String!
price: Money!
inventory: Int!
}
Design Decisions
When to Use What
Returning a list?
├── Small fixed size (<20) → [Item!]!
└── Variable/large size → ItemConnection!
Mutation result?
├── Can have user errors → Payload pattern
└── System errors only → Direct return
Multiple possible types?
├── Completely different → Union
└── Share common fields → Interface
Nested data?
├── Always needed together → Embed
└── Sometimes needed → Separate + ID reference
Nullability Strategy
type User {
# Always required
id: ID!
email: String!
# Optional (user choice)
nickname: String
bio: String
# Lists: require list, require items
posts: [Post!]! # Never null, items never null
# Computed (may fail)
avatar: String # Nullable if generation can fail
}
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Breaking change | Removed field | Use @deprecated first |
| Over-fetching | No pagination | Add Connection pattern |
| N+1 queries | Direct relations | Use DataLoader |
| Type explosion | Too many types | Use interfaces/generics |
Schema Health Check
# Validate
npx graphql-inspector validate schema.graphql
# Check breaking changes
npx graphql-inspector diff old.graphql new.graphql
# Coverage analysis
npx graphql-inspector coverage schema.graphql queries/*.graphql
Usage
Skill("graphql-schema-design")
Related Skills
graphql-fundamentals- Basic types and syntaxgraphql-resolvers- Implementing the schemagraphql-security- Auth-aware design
Related Agent
02-graphql-schema- For detailed guidance
You Might Also Like
Related Skills

gog
169Kdev-api
Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.
openclaw
orpc-contract-first
127Kdev-api
Guide for implementing oRPC contract-first API patterns in Dify frontend. Triggers when creating new API contracts, adding service endpoints, integrating TanStack Query with typed contracts, or migrating legacy service calls to oRPC. Use for all API layer work in web/contract and web/service directories.
langgenius
