GRAPHQL Design
GraphQL schema design patterns for building flexible, efficient, and evolvable query APIs
You are an expert in GraphQL schema design for designing robust APIs. ## Key Points - Use non-nullable types (`!`) by default and only make fields nullable when null is a meaningful value. - Name mutations as verb-noun pairs (`createUser`, `cancelOrder`) and always return payload types. - Implement query complexity analysis and depth limiting to prevent abusive queries. - Exposing database structure directly in the schema instead of modeling a client-oriented graph. - Neglecting the N+1 query problem by resolving nested fields with individual database calls.
skilldb get api-design-skills/GRAPHQL DesignFull skill: 166 linesGraphQL Design — API Design
You are an expert in GraphQL schema design for designing robust APIs.
Core Philosophy
Overview
GraphQL is a query language and runtime that gives clients the power to request exactly the data they need. A well-designed schema acts as a contract between frontend and backend, enabling independent evolution of both sides.
Core Concepts
Schema-First Design
Define your schema as the source of truth before writing resolvers.
type User {
id: ID!
name: String!
email: String!
orders(first: Int, after: String): OrderConnection!
}
type Order {
id: ID!
total: Float!
status: OrderStatus!
createdAt: DateTime!
}
enum OrderStatus {
PENDING
SHIPPED
DELIVERED
CANCELLED
}
Input Types
Use dedicated input types for mutations to separate read and write shapes.
input CreateUserInput {
name: String!
email: String!
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
}
type CreateUserPayload {
user: User
errors: [UserError!]!
}
Connections and Pagination
Follow the Relay connection specification for paginated lists.
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type OrderEdge {
node: Order!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
Implementation Patterns
Mutation Payloads
Always return a payload type from mutations with both the result and structured errors.
type CreateUserPayload {
user: User
errors: [UserError!]!
}
type UserError {
field: String!
message: String!
code: ErrorCode!
}
DataLoader for N+1 Prevention
Batch and cache database lookups within a single request.
const userLoader = new DataLoader(async (ids) => {
const users = await db.users.findByIds(ids);
const userMap = new Map(users.map(u => [u.id, u]));
return ids.map(id => userMap.get(id) || null);
});
const resolvers = {
Order: {
user: (order, _, { loaders }) => loaders.user.load(order.userId),
},
};
Schema Stitching and Federation
For microservices, use Apollo Federation to compose a unified graph.
# Users service
type User @key(fields: "id") {
id: ID!
name: String!
}
# Orders service
extend type User @key(fields: "id") {
id: ID! @external
orders: [Order!]!
}
Best Practices
- Use non-nullable types (
!) by default and only make fields nullable when null is a meaningful value. - Name mutations as verb-noun pairs (
createUser,cancelOrder) and always return payload types. - Implement query complexity analysis and depth limiting to prevent abusive queries.
Common Pitfalls
- Exposing database structure directly in the schema instead of modeling a client-oriented graph.
- Neglecting the N+1 query problem by resolving nested fields with individual database calls.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add api-design-skills
Related Skills
API Authentication
API authentication patterns including OAuth 2.0, JWT, and API keys for securing HTTP APIs
API Documentation
OpenAPI and Swagger documentation practices for generating accurate, maintainable API references
API Error Handling
Error response design and HTTP status code conventions for consistent, actionable API error reporting
API Pagination
Pagination patterns including cursor-based, offset, and keyset pagination for efficient list endpoints
API Versioning
API versioning strategies for evolving APIs without breaking existing consumers
REST Design
RESTful API design principles for building consistent, intuitive, and scalable HTTP APIs