Complete AI prompt library for GraphQL developers. Covers schema design, resolvers, authentication, DataLoader for N+1 prevention, subscriptions, Federation for microservices, persisted queries, testing, and deployment with Apollo Server or Yoga.
GraphQL in 2026: Flexible APIs at Scale
GraphQL enables clients to request exactly the data they need, making it ideal for complex frontend applications, mobile clients with bandwidth constraints, and microservice federation. The teams shipping the most reliable GraphQL APIs use code-first TypeScript schemas, DataLoaders on every relationship, and shield-based authorization β patterns that AI generates perfectly when prompted correctly.
1. Code-First GraphQL Schema with Pothos
You are a GraphQL expert using Pothos (formerly GiraphQL) with TypeScript.
Build a type-safe GraphQL schema for a project management API:
Types to define:
- User, Organization, Project, Task, Comment, PageInfo, Connection types
- Task: id, title, description, status (enum), priority (enum), dueDate, assignedTo (User), project (Project), comments (CommentConnection), createdAt, updatedAt
Pothos setup:
- builder = new SchemaBuilder with: context type (user, organizationId, dataLoaders), plugins: [SimpleObjectsPlugin, RelayPlugin, ValidationPlugin, ScopeAuthPlugin]
- Relay plugin: automatic Connection and Edge types, cursor-based pagination on all list fields
- Enum types: TaskStatus (TODO, IN_PROGRESS, DONE, CANCELLED), Priority (LOW, MEDIUM, HIGH, CRITICAL)
Queries:
- task(id: ID!): Task β @auth(requires: AUTHENTICATED)
- tasks(filter: TaskFilterInput, first: Int, after: String): TaskConnection β paginated
- myTasks: [Task!]! β current user's assigned tasks
Mutations:
- createTask(input: CreateTaskInput!): CreateTaskPayload β returns task or errors array
- updateTask(id: ID!, input: UpdateTaskInput!): UpdateTaskPayload
- assignTask(taskId: ID!, userId: ID!): AssignTaskPayload
Output: schema builder setup, type definitions, query/mutation resolvers, and generated schema SDL.
2. DataLoader for N+1 Prevention
You are a GraphQL performance expert specialising in DataLoader patterns.
Build a complete DataLoader setup for a GraphQL API to eliminate N+1 queries:
DataLoaders required (one per relationship):
- userLoader: batch load users by ID β Map<string, User>
- projectLoader: batch load projects by ID
- tasksByProjectLoader: batch load tasks by project ID β Map<string, Task[]>
- commentsByTaskLoader: batch load comments by task ID
- assignedTasksLoader: batch load tasks assigned to each user ID
Each DataLoader:
- Created per GraphQL request (in context factory β not global singletons)
- Batch function: single DB query with WHERE id = ANY($1) (PostgreSQL) or IN clause
- Cache: DataLoader's built-in per-request cache (deduplicates within one request)
- Max batch size: 100 (prevent overly large IN clauses)
- Error handling: return Error instance for missing IDs (DataLoader per-item error)
Context factory:
function createContext(req): Context {
return {
user: extractUserFromJWT(req),
db: dbConnection,
loaders: {
user: new DataLoader(batchLoadUsers),
project: new DataLoader(batchLoadProjects),
tasksByProject: new DataLoader(batchLoadTasksByProject),
// ...
}
}
}
Show: all DataLoader batch functions with SQL, context factory, and resolver usage in Task.assignedTo and Project.tasks fields.
CRITICAL: explain why DataLoaders must be created per-request, not per-server.
Why it works: The "per-request, not per-server" requirement prevents the most catastrophic DataLoader mistake β a shared cache that leaks data between users. This single line prevents a serious security vulnerability.
3. Authentication & Authorization with Directives
You are a GraphQL security architect.
Implement field-level authentication and authorization for a GraphQL API:
Authentication (Apollo Server context):
- Extract JWT from Authorization header in contextFunction
- Verify RS256 signature, check expiry
- Attach to context: { user: { id, organizationId, role } | null }
- Never throw in context creation β let resolvers decide what's public
Authorization approach β Schema Directives:
- @auth(requires: Role) directive on type fields
- @ownerOnly β resolver checks resource.userId === context.user.id
- @rateLimit(max: Int, window: String) β per-user field rate limiting
Directive implementation:
- mapSchema + MapperKind to wrap resolver functions at schema build time
- Auth directive: if context.user is null or wrong role β throw AuthenticationError (Apollo error code)
- Tenant isolation: every resolver that touches DB must filter by context.user.organizationId
Error handling:
- Distinguish: AuthenticationError (unauthenticated) vs ForbiddenError (wrong role)
- Never expose internal resolver errors β mask with "Internal server error" + log full error with request ID
- Extensions: include error code in { extensions: { code: 'UNAUTHENTICATED' } }
Output: directive definitions, mapSchema implementation, and 5 examples on type fields.
4. GraphQL Subscriptions
You are a GraphQL real-time expert.
Implement GraphQL subscriptions for real-time task updates:
Subscriptions:
- taskUpdated(projectId: ID!): Task β emits when any task in the project is updated
- taskAssigned(userId: ID!): Task β emits when a task is assigned to the user
- projectActivity(projectId: ID!): ActivityEvent β all project events stream
PubSub setup:
- graphql-redis-subscriptions with Redis for horizontal scaling (not in-memory PubSub)
- Channel naming: task:updated:{projectId}, task:assigned:{userId}
- Authorization in subscription resolver: verify user can access the projectId before subscribing
Publishing from mutations:
- After createTask mutation: pubsub.publish('task:updated:{projectId}', { taskUpdated: task })
- After assignTask mutation: pubsub.publish('task:assigned:{userId}', { taskAssigned: task })
WebSocket setup (Apollo Server + graphql-ws):
- wsServer: separate WebSocket server on same HTTP server
- Auth: extract JWT from connection params (not headers β WebSocket handshake headers are limited)
- Cleanup: on disconnect, close any open DB queries or subscriptions
Output: subscription type definitions, resolver with asyncIterator, Redis PubSub config, and wsServer setup.
5. Apollo Federation for Microservices
You are an Apollo Federation expert.
Design a federated GraphQL schema across 3 microservices:
Subgraph 1 β users-service (owns User type):
- User @key(fields: "id"): id, email, name, role, createdAt
- Query: me, user(id: ID!), users(filter: UserFilter): [User!]!
Subgraph 2 β projects-service (owns Project, Task types):
- Project @key(fields: "id"): id, name, description, owner: User (reference)
- Task @key(fields: "id"): id, title, status, assignedTo: User (reference), project: Project
- Extends User @key(fields: "id") to add: assignedTasks: [Task!]! (defined in this subgraph)
Subgraph 3 β notifications-service (owns Notification type):
- Notification @key(fields: "id"): id, type, message, recipient: User (reference), read
Apollo Router (supergraph) config:
- Compose subgraph schemas with rover subgraph introspect
- JWT validation at router level (propagate as x-user-id, x-organization-id headers to subgraphs)
- Query plan caching
- Response caching with @cacheControl directives
Entity resolution:
- Each subgraph implements __resolveReference for its @key types
- Show how User is resolved across services without a shared DB
Output: all 3 subgraph schemas with @key, reference resolvers, and supergraph YAML config.
6. GraphQL Testing
You are a GraphQL testing expert.
Write comprehensive tests for a GraphQL task API using Jest:
Test setup:
- ApolloServer in test mode: new ApolloServer({ schema }) + startStandaloneServer or executeOperation
- In-memory test context factory: mock DataLoaders, seed database via Prisma test client
- JWT factory: createTestToken(userId, role, organizationId)
Test operations (use gql tagged template literals):
Query tests:
1. task query: authenticated, returns full task with nested assignee (DataLoader called once β assert DB call count)
2. task query: returns null for other org's task (tenant isolation)
3. task query: unauthenticated β extensions.code === 'UNAUTHENTICATED'
Mutation tests:
4. createTask: valid input β task created in DB, subscription event published
5. createTask: validation failure β errors array with field-level messages (not a single error)
6. createTask: assignee from different org β FORBIDDEN error code
DataLoader test:
7. Fetch 5 tasks in one query β assert assignee DataLoader batch function called exactly once (spy on DB)
Output: Jest setup, reusable test helpers, and all 7 test cases with gql operations.
7. Good vs Bad GraphQL Prompts
| Task | β Bad Prompt | β Good Prompt |
|---|---|---|
| Schema design | "Build a GraphQL API for tasks" | "Build a Pothos code-first TypeScript GraphQL schema for Task: Relay connection pagination on tasks query, TaskStatus enum, assignedTo resolver using DataLoader (batch by ID, created per-request in context), @auth directive on all mutations. Return mutation payload type with task and errors fields." |
| N+1 prevention | "Load task assignees" | "Build userDataLoader for GraphQL context: batch function fetches all user IDs in one SELECT WHERE id = ANY($1) query, max batch size 100, created per-request (not global). Show how Task.assignedTo resolver calls context.loaders.user.load(task.assignedToId)." |
| Authorization | "Add auth to my GraphQL API" | "Implement @auth(requires: ADMIN) schema directive with mapSchema: wrap resolver, check context.user.role, throw ForbiddenError with extensions.code='FORBIDDEN' if denied. Tenant isolation: every DB query filters by context.user.organizationId. Auth in context, not in resolvers." |
Generate a custom GraphQL prompt β Try PromptPrepare free
Found this helpful? Share it.