skill:graphql-design — GraphQL Schema Design, Resolvers, and Federation
Version: 1.0.0
Purpose
Design production-grade GraphQL APIs with well-structured schemas, efficient resolvers, and scalable federation strategies. This skill guides the design of type systems, query structures, mutation patterns, and subscription models — producing schema definitions, resolver architecture maps, and performance optimization recommendations.
Use when:
-
Designing a new GraphQL API from scratch
-
Migrating from REST to GraphQL
-
Implementing schema federation across multiple services
-
Optimizing an existing GraphQL API for performance
-
Establishing GraphQL design standards for an organization
-
Adding real-time features with GraphQL subscriptions
File Structure
skills/graphql-design/ ├── SKILL.md (this file) └── examples.md
Interface References
-
Context: Loaded via ContextProvider Interface
-
Memory: Accessed via MemoryStore Interface
-
Shared Patterns: Shared Loading Patterns
-
Schemas: Validated against context_metadata.schema.json and memory_entry.schema.json
MANDATORY WORKFLOW (MUST FOLLOW EXACTLY)
IMPORTANT: Execute ALL steps in order. Do not skip any step.
Step 1: Identify GraphQL API Requirements
YOU MUST:
-
Determine the API scope:
-
New GraphQL API (greenfield)
-
REST-to-GraphQL migration
-
Schema extension (adding types/fields to existing schema)
-
Federation design (multi-service graph)
-
Identify consumers:
-
Web SPA (React, Vue, Angular)
-
Mobile apps (iOS/Android)
-
Server-to-server
-
Third-party developers (public API)
-
Determine data characteristics:
-
Entity types and their relationships
-
Read vs. write ratio
-
Real-time requirements (subscriptions needed?)
-
Data volume and query complexity expectations
-
Clarify constraints:
-
Existing data sources (databases, REST APIs, microservices)
-
Authentication mechanism
-
Rate limiting and query complexity budget
-
Schema federation requirements
DO NOT PROCEED WITHOUT A CLEAR SCOPE
Step 2: Load Memory
Follow Standard Memory Loading with skill="graphql-design" and domain="engineering" .
YOU MUST:
-
Use memoryStore.getSkillMemory("graphql-design", "{project-name}") to load existing schema conventions
-
Use memoryStore.getByProject("{project-name}") for cross-skill insights
-
If memory exists, adopt established naming conventions, pagination patterns, and error handling
-
If no memory exists, proceed and create it in Step 8
Step 3: Load Context
Follow Standard Context Loading for the engineering domain. Stay within the file budget declared in frontmatter.
Step 4: Design Schema Types
YOU MUST:
- Define entity types (schema-first approach): type User { id: ID! email: String! name: String! role: UserRole! createdAt: DateTime! orders(first: Int, after: String): OrderConnection! }
enum UserRole { ADMIN EDITOR VIEWER }
-
Follow naming conventions:
-
Types: PascalCase (User , OrderItem )
-
Fields: camelCase (firstName , createdAt )
-
Enums: SCREAMING_SNAKE_CASE (USER_ROLE , ORDER_STATUS )
-
Mutations: verb + noun (createUser , updateOrder , cancelSubscription )
-
Queries: noun for single (user ), plural or connection for lists (users , ordersConnection )
-
Use explicit nullability:
-
String! — non-nullable (guaranteed to be present)
-
String — nullable (may be null)
-
Default to non-nullable; use nullable only when null has semantic meaning
-
Define custom scalars where needed:
-
DateTime — ISO 8601 timestamps
-
URL — Validated URL strings
-
EmailAddress — Validated email format
-
JSON — Arbitrary JSON (use sparingly)
Step 5: Design Queries, Mutations, and Subscriptions
YOU MUST:
-
Queries — Read operations: type Query {
Single entity by ID
user(id: ID!): User
Connection-based pagination (Relay spec)
users(first: Int, after: String, filter: UserFilter): UserConnection!
Search
searchUsers(query: String!, first: Int): UserConnection! }
-
Mutations — Write operations with input types and payload types: input CreateUserInput { email: String! name: String! role: UserRole = VIEWER }
type CreateUserPayload { user: User errors: [UserError!]! }
type Mutation { createUser(input: CreateUserInput!): CreateUserPayload! updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload! deleteUser(id: ID!): DeleteUserPayload! }
-
Subscriptions — Real-time updates: type Subscription { orderStatusChanged(orderId: ID!): Order! newMessage(channelId: ID!): Message! }
-
Pagination — Relay Connection specification: type UserConnection { edges: [UserEdge!]! pageInfo: PageInfo! totalCount: Int! }
type UserEdge { cursor: String! node: User! }
type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String }
- Error handling — Typed errors in mutation payloads: interface UserError { message: String! path: [String!] }
type ValidationError implements UserError { message: String! path: [String!] field: String! constraint: String! }
type AuthorizationError implements UserError { message: String! path: [String!] requiredRole: UserRole! }
Step 6: Design Resolver Architecture
YOU MUST address:
-
N+1 Prevention — DataLoader pattern:
-
Batch all database lookups by parent type
-
One DataLoader per entity type per request
-
DataLoader instances are request-scoped (never shared)
-
Authorization — Field-level and type-level:
-
Directive-based: @auth(requires: ADMIN)
-
Resolver middleware for complex authorization logic
-
Never expose unauthorized data even if the field is requested
-
Query complexity analysis:
-
Assign cost to each field (default 1, connections higher)
-
Set maximum query complexity budget (e.g., 1000)
-
Reject queries exceeding the budget before execution
-
Limit query depth (e.g., max 10 levels)
-
Federation (if multi-service):
-
Define entity ownership: which service is the source of truth
-
Use @key directive for entity references across services
-
Extend types from other services with extend type
-
Gateway composes the unified graph
Step 7: Generate Output
-
Save output to /claudedocs/graphql-design_{project}_{YYYY-MM-DD}.md
-
Follow naming conventions in ../OUTPUT_CONVENTIONS.md
-
Include:
-
Complete SDL (Schema Definition Language) file
-
Type inventory with descriptions
-
Resolver architecture (DataLoader strategy, auth rules)
-
Query complexity budget
-
Example queries and mutations with expected responses
-
Federation entity map (if applicable)
Step 8: Update Memory
Follow Standard Memory Update for skill="graphql-design" .
Store:
-
schema_conventions.md: Naming rules, pagination style, error handling pattern, custom scalars
-
project_overview.md: Schema scope, entity inventory, federation topology, data sources
GraphQL Design Principles
Principle Guideline
Schema-first Design the schema before writing resolvers
Client-driven Schema should serve client needs, not mirror database tables
Explicit nullability Every field's nullability should be a deliberate decision
Single source of truth Each entity type is owned by exactly one service (federation)
Demand-driven Only add fields that clients actually need
Evolvable Deprecate fields instead of removing them; add new fields freely
Common Anti-Patterns to Prevent
Anti-Pattern Correct Approach
CRUD-mapped mutations (createUser , getUser ) Design around domain operations (registerUser , inviteTeamMember )
Returning raw database errors Use typed error unions in mutation payloads
Unbounded list queries Always paginate with connections
N+1 queries in resolvers Use DataLoader for batched data fetching
Nullable everything Default to non-nullable; null means "intentionally absent"
Generic JSON scalar overuse Define proper types; JSON hides schema information
No query depth/complexity limits Set max depth (10) and complexity budget (1000)
Compliance Checklist
Before completing, verify:
-
Step 1: API scope, consumers, data characteristics, and constraints identified
-
Step 2: Standard Memory Loading pattern followed
-
Step 3: Standard Context Loading pattern followed
-
Step 4: Schema types designed with proper naming, nullability, and custom scalars
-
Step 5: Queries, mutations, subscriptions, pagination, and error handling defined
-
Step 6: Resolver architecture — DataLoader, auth, complexity analysis, federation addressed
-
Step 7: Output saved with standard naming convention
-
Step 8: Standard Memory Update pattern followed
FAILURE TO COMPLETE ALL STEPS INVALIDATES THE DESIGN
Further Reading
-
GraphQL Specification: https://spec.graphql.org/
-
Relay Connection Specification: https://relay.dev/graphql/connections.htm
-
Apollo Federation: https://www.apollographql.com/docs/federation/
-
GraphQL Best Practices: https://graphql.org/learn/best-practices/
-
Production GraphQL by Marc-André Giroux
Version History
Version Date Changes
1.0.0 2026-02-12 Initial release — schema design, resolvers, federation, pagination, error handling, N+1 prevention