Advisor Graphql Architect
All-in-one agent covering agent, designing, evolving, graphql. Includes structured workflows, validation checks, and reusable patterns for api graphql.
Advisor GraphQL Architect
An autonomous agent that designs performant GraphQL schemas — defining types, resolvers, query optimization, N+1 prevention with DataLoader, and federated gateway architectures for complex data graphs.
When to Use This Agent
Choose Advisor GraphQL Architect when:
- You are building a new GraphQL API and need schema design guidance
- Existing GraphQL queries are slow due to N+1 problems or over-fetching
- You need to implement GraphQL federation across multiple services
- You want subscription support for real-time data updates
Consider alternatives when:
- Your API has simple CRUD with a single consumer (REST is simpler)
- You need binary protocol efficiency for microservice communication (use gRPC)
- You are adding GraphQL to a project that already has a mature REST API (evaluate carefully)
Quick Start
# .claude/agents/graphql-architect.yml name: advisor-graphql-architect description: Design performant GraphQL schemas and architectures agent_prompt: | You are a GraphQL Architect. When designing GraphQL systems: 1. Model the domain as a graph of interconnected types 2. Design queries for client use cases, not database tables 3. Implement DataLoader for batched, cached data loading 4. Add query complexity analysis to prevent abuse 5. Set up persisted queries for production security 6. Design mutations with input types and payload types Performance rules: - ALWAYS use DataLoader for any relationship resolver - Limit query depth to 7 levels max - Set query complexity limits (cost per field) - Use cursor pagination, never offset
Example invocation:
claude "Design a GraphQL schema for a project management app with users, projects, tasks, and comments"
Sample schema design:
type Query { viewer: User! project(id: ID!): Project projects(first: Int!, after: String, filter: ProjectFilter): ProjectConnection! } type User { id: ID! name: String! email: String! projects(first: Int!, after: String): ProjectConnection! assignedTasks(status: TaskStatus): [Task!]! } type Project { id: ID! name: String! owner: User! members: [User!]! tasks(first: Int!, after: String, status: TaskStatus): TaskConnection! stats: ProjectStats! } type Task { id: ID! title: String! status: TaskStatus! assignee: User project: Project! comments(first: Int!, after: String): CommentConnection! createdAt: DateTime! updatedAt: DateTime! }
Core Concepts
Schema Design Principles
| Principle | Rule | Example |
|---|---|---|
| Graph thinking | Model relationships, not tables | task.assignee not task.assigneeId |
| Client-driven | Design for UI components | viewer.dashboard aggregation query |
| Connections | Cursor-based pagination | ProjectConnection { edges, pageInfo } |
| Input types | Structured mutation inputs | input CreateTaskInput { title, assigneeId } |
| Payload types | Rich mutation responses | type CreateTaskPayload { task, errors } |
DataLoader Pattern (N+1 Prevention)
// Without DataLoader: N+1 queries // Query: { projects { owner { name } } } // → 1 query for projects + N queries for each owner // With DataLoader: 2 queries total import DataLoader from 'dataloader'; const userLoader = new DataLoader(async (userIds: string[]) => { const users = await db.users.findMany({ where: { id: { in: userIds } } }); // Return in same order as requested IDs return userIds.map(id => users.find(u => u.id === id) || null); }); const resolvers = { Project: { owner: (project) => userLoader.load(project.ownerId), }, Task: { assignee: (task) => task.assigneeId ? userLoader.load(task.assigneeId) : null, } };
Query Complexity Analysis
// Prevent expensive queries from consuming resources import { createComplexityLimitRule } from 'graphql-validation-complexity'; const complexityRule = createComplexityLimitRule(1000, { scalarCost: 1, objectCost: 2, listFactor: 10, // Multiply cost by expected list size introspectionListFactor: 2, // Custom field costs formatComplexity: ({ type, field, args }) => { if (field.name === 'search') return 50; // Search is expensive if (args.first > 100) return args.first; // Large pages cost more return undefined; // Use default } }); // Reject queries exceeding cost 1000 // Example costs: // { viewer { name } } = 3 // { projects(first: 20) { edges { node { tasks(first: 50) { ... } } } } } = 1000+
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
maxQueryDepth | number | 7 | Maximum nesting depth for queries |
maxQueryComplexity | number | 1000 | Maximum query cost score |
enableDataLoader | boolean | true | Auto-generate DataLoader recommendations |
paginationStyle | string | "relay" | Pagination: relay-connections, simple-cursor |
enableSubscriptions | boolean | false | Include subscription type design |
federation | boolean | false | Design for Apollo Federation |
Best Practices
-
Use DataLoader for every relationship resolver — Even if you think a resolver will only be called once, wrap it in DataLoader. As your schema grows and queries become more complex, resolvers that were called once will be called in lists. DataLoader batches and caches by default, so there is zero overhead for single calls.
-
Design mutations with input/payload types — Every mutation should accept a single
inputargument and return a payload type that includes both the result and potential user-facing errors. This pattern scales well, supports field-level validation errors, and makes schema evolution easier. -
Implement query complexity limits before production — Without limits, a single malicious or careless query can load your entire database through deeply nested relationships. Set field costs, list multipliers, and a maximum total complexity. Reject queries exceeding the limit before execution.
-
Use Relay-style connections for all paginated fields — The
Connection { edges { node, cursor }, pageInfo { hasNextPage, endCursor } }pattern is verbose but proven. It supports cursor-based pagination, carries edge metadata, and works with every GraphQL client pagination library. -
Never expose internal IDs or database structure — Use opaque IDs (base64-encoded
Type:ID), avoid field names that match database column names exactly (usecreatedAtnotcreated_at), and never expose fields like_id,rowId, orsequence. The schema should model the domain, not the database.
Common Issues
N+1 queries despite using DataLoader — DataLoader batches within a single tick of the event loop. If your resolvers use await between loading related data, the batching window may close before all keys are collected. Ensure all DataLoader .load() calls happen synchronously within the resolver, and use Promise.all() for multiple loads.
Schema becomes too large to navigate — As the schema grows to hundreds of types, developers struggle to find the right queries. Organize the schema into logical domains (User, Project, Billing), use schema stitching or federation to split the implementation across files, and maintain an interactive GraphQL explorer (like GraphiQL or Apollo Studio) as the primary documentation.
Subscriptions cause memory leaks under load — Each subscription holds an open WebSocket connection and a database change listener. Under high load, these listeners accumulate and consume memory. Implement connection limits per user, automatic subscription cleanup on disconnect, and heartbeat mechanisms to detect and close stale connections.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
API Endpoint Builder
Agent that scaffolds complete REST API endpoints with controller, service, route, types, and tests. Supports Express, Fastify, and NestJS.
Documentation Auto-Generator
Agent that reads your codebase and generates comprehensive documentation including API docs, architecture guides, and setup instructions.
Ai Ethics Advisor Partner
All-in-one agent covering ethics, responsible, development, specialist. Includes structured workflows, validation checks, and reusable patterns for ai specialists.