Senior GraphQL Specialist
Expert GraphQL API design and architecture skill for building scalable, type-safe APIs with Apollo Server, Federation, and modern GraphQL patterns.
Overview
This skill provides comprehensive GraphQL development capabilities including schema design, resolver implementation, federation architecture, real-time subscriptions, and performance optimization through DataLoader patterns.
Time Savings: 50%+ reduction in GraphQL API development time through schema generation, resolver scaffolding, and automated federation setup.
Quality Improvement: 40%+ improvement in API consistency through schema-first development, type safety enforcement, and automated best practices.
Core Capabilities
Schema Architecture
- Schema-first design methodology
- Type system design (scalars, enums, interfaces, unions)
- Input type and argument patterns
- Custom directive implementation
- Schema stitching and composition
Resolver Development
- Resolver pattern implementation
- Context and middleware integration
- Authentication/authorization in resolvers
- Error handling and formatting
- N+1 query prevention with DataLoader
Apollo Federation
- Supergraph architecture design
- Subgraph creation and entity definitions
@key, @external, @requires directive usage
- Gateway configuration
- Schema composition validation
Performance Optimization
- Query complexity analysis and limiting
- Depth limiting implementation
- Caching strategies (Apollo Cache, Redis)
- Batching with DataLoader
- Persisted queries
Real-time Features
- Subscription implementation
- WebSocket configuration
- PubSub patterns
- Filtered subscriptions
Quick Start
# Analyze existing GraphQL schema
python scripts/schema_analyzer.py schema.graphql --output json
# Generate resolvers from schema
python scripts/resolver_generator.py schema.graphql --output src/resolvers
# Scaffold Apollo Federation subgraph
python scripts/federation_scaffolder.py users-service --entities User,Profile
Key Workflows
1. Schema-First API Design
Goal: Design a type-safe GraphQL schema following best practices.
Steps:
Analyze Requirements
- Identify domain entities and relationships
- Map CRUD operations to queries/mutations
- Define subscription needs for real-time features
Design Schema
# Types with clear naming conventions
type User {
id: ID!
email: String!
profile: Profile
posts(first: Int, after: String): PostConnection!
createdAt: DateTime!
}
# Relay-style pagination
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type PostEdge {
node: Post!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# Input types for mutations
input CreateUserInput {
email: String!
name: String!
password: String!
}
# Clear query/mutation organization
type Query {
user(id: ID!): User
users(first: Int, after: String): UserConnection!
me: User
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
}
# Subscription for real-time
type Subscription {
userCreated: User!
postPublished(authorId: ID): Post!
}
Validate Schema
python scripts/schema_analyzer.py schema.graphql --validate
Generate Resolvers
python scripts/resolver_generator.py schema.graphql --output src/resolvers --typescript
Success Criteria:
- Schema passes validation
- All types have descriptions
- Relay pagination implemented for lists
- Input types for all mutations
- Clear naming conventions followed
2. DataLoader Implementation for N+1 Prevention
Goal: Eliminate N+1 queries using DataLoader batching.
Problem Example:
# This query would cause N+1 without DataLoader
query {
posts { # 1 query for posts
author { # N queries for authors (one per post!)
name
}
}
}
Solution:
Create DataLoader Factory
// src/dataloaders/index.ts
import DataLoader from 'dataloader';
import { prisma } from '../lib/prisma';
export const createLoaders = () => ({
userLoader: new DataLoader<string, User>(async (userIds) => {
const users = await prisma.user.findMany({
where: { id: { in: [...userIds] } }
});
// Return in same order as requested IDs
const userMap = new Map(users.map(u => [u.id, u]));
return userIds.map(id => userMap.get(id) || null);
}),
postsByAuthorLoader: new DataLoader<string, Post[]>(async (authorIds) => {
const posts = await prisma.post.findMany({
where: { authorId: { in: [...authorIds] } }
});
// Group posts by authorId
const postMap = new Map<string, Post[]>();
posts.forEach(post => {
const existing = postMap.get(post.authorId) || [];
existing.push(post);
postMap.set(post.authorId, existing);
});
return authorIds.map(id => postMap.get(id) || []);
}),
});
export type Loaders = ReturnType<typeof createLoaders>;
Add Loaders to Context
// src/server.ts
import { createLoaders } from './dataloaders';
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
user: authenticateToken(req),
loaders: createLoaders(), // Fresh loaders per request
}),
});
Use in Resolvers
// src/resolvers/post.resolver.ts
export const PostResolvers = {
Post: {
author: (parent, _, { loaders }) => {
return loaders.userLoader.load(parent.authorId);
},
},
};
Verify Batching
- Enable query logging
- Run test query
- Confirm single batch query instead of N queries
Success Criteria:
- Batch queries visible in logs
- Query count reduced from N+1 to 2
- Response time improved significantly
- DataLoader cache cleared per request
3. Apollo Federation Setup
Goal: Build a federated supergraph from multiple subgraphs.
Architecture:
┌─────────────────────────────────────────────────┐
│ Apollo Gateway │
│ (Schema Composition) │
└─────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Users │ │ Posts │ │ Comments │
│ Subgraph │ │ Subgraph │ │ Subgraph │
└─────────────┘ └─────────────┘ └─────────────┘
Steps:
Scaffold Subgraphs
# Create users subgraph
python scripts/federation_scaffolder.py users-service \
--entities User,Profile \
--port 4001
# Create posts subgraph
python scripts/federation_scaffolder.py posts-service \
--entities Post \
--references User \
--port 4002
# Create comments subgraph
python scripts/federation_scaffolder.py comments-service \
--entities Comment \
--references User,Post \
--port 4003
Define Entity References
# users-service/schema.graphql
type User @key(fields: "id") {
id: ID!
email: String!
name: String!
profile: Profile
}
# posts-service/schema.graphql
type Post @key(fields: "id") {
id: ID!
title: String!
content: String!
author: User!
}
# Extend User to add posts field
extend type User @key(fields: "id") {
id: ID! @external
posts: [Post!]!
}
# comments-service/schema.graphql
type Comment @key(fields: "id") {
id: ID!
content: String!
author: User!
post: Post!
}
extend type Post @key(fields: "id") {
id: ID! @external
comments: [Comment!]!
}
Implement Reference Resolvers
// posts-service/resolvers.ts
export const resolvers = {
User: {
__resolveReference: async (user, { dataSources }) => {
// Return only the fields this subgraph owns
return { id: user.id };
},
posts: async (user, _, { dataSources }) => {
return dataSources.postsAPI.getPostsByAuthor(user.id);
},
},
Post: {
__resolveReference: async (post, { dataSources }) => {
return dataSources.postsAPI.getPost(post.id);
},
author: (post) => {
// Return reference for gateway to resolve
return { __typename: 'User', id: post.authorId };
},
},
};
Configure Gateway
// gateway/index.ts
import { ApolloGateway, IntrospectAndCompose } from '@apollo/gateway';
import { ApolloServer } from '@apollo/server';
const gateway = new ApolloGateway({
supergraphSdl: new IntrospectAndCompose({
subgraphs: [
{ name: 'users', url: 'http://localhost:4001/graphql' },
{ name: 'posts', url: 'http://localhost:4002/graphql' },
{ name: 'comments', url: 'http://localhost:4003/graphql' },
],
}),
});
const server = new ApolloServer({ gateway });
Test Federated Query
query FederatedQuery {
user(id: "123") {
id
name
posts {
id
title
comments {
content
author {
name # Resolves back to users subgraph
}
}
}
}
}
Success Criteria:
- All subgraphs start without errors
- Schema composition succeeds
- Cross-subgraph queries resolve correctly
- Entity references work bidirectionally
4. Real-time Subscriptions
Goal: Implement GraphQL subscriptions for real-time updates.
Steps:
Configure WebSocket Server
// src/server.ts
import { createServer } from 'http';
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import { ApolloServer } from '@apollo/server';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
const httpServer = createServer(app);
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql',
});
const serverCleanup = useServer(
{
schema,
context: (ctx) => ({
user: authenticateWebSocket(ctx.connectionParams),
}),
},
wsServer
);
const server = new ApolloServer({
schema,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
},
],
});
Implement PubSub
// src/pubsub.ts
import { PubSub } from 'graphql-subscriptions';
import { RedisPubSub } from 'graphql-redis-subscriptions';
// For production, use Redis PubSub
export const pubsub = new RedisPubSub({
connection: process.env.REDIS_URL,
});
// Event types
export const EVENTS = {
POST_CREATED: 'POST_CREATED',
POST_UPDATED: 'POST_UPDATED',
COMMENT_ADDED: 'COMMENT_ADDED',
USER_ONLINE: 'USER_ONLINE',
};
Define Subscription Schema
type Subscription {
postCreated: Post!
postUpdated(id: ID!): Post!
commentAdded(postId: ID!): Comment!
userPresence(roomId: ID!): UserPresenceEvent!
}
type UserPresenceEvent {
user: User!
status: PresenceStatus!
}
enum PresenceStatus {
ONLINE
OFFLINE
AWAY
}
Implement Subscription Resolvers
// src/resolvers/subscription.resolver.ts
import { withFilter } from 'graphql-subscriptions';
import { pubsub, EVENTS } from '../pubsub';
export const SubscriptionResolvers = {
Subscription: {
postCreated: {
subscribe: () => pubsub.asyncIterator([EVENTS.POST_CREATED]),
},
postUpdated: {
subscribe: withFilter(
() => pubsub.asyncIterator([EVENTS.POST_UPDATED]),
(payload, variables) => {
return payload.postUpdated.id === variables.id;
}
),
},
commentAdded: {
subscribe: withFilter(
() => pubsub.asyncIterator([EVENTS.COMMENT_ADDED]),
(payload, variables, context) => {
// Only notify if user has access to the post
return payload.commentAdded.postId === variables.postId;
}
),
},
},
};
Publish Events
// src/resolvers/mutation.resolver.ts
export const MutationResolvers = {
Mutation: {
createPost: async (_, { input }, { user, prisma }) => {
const post = await prisma.post.create({
data: { ...input, authorId: user.id },
});
// Publish to subscribers
await pubsub.publish(EVENTS.POST_CREATED, { postCreated: post });
return { post };
},
addComment: async (_, { input }, { user, prisma }) => {
const comment = await prisma.comment.create({
data: { ...input, authorId: user.id },
});
// Publish to subscribers watching this post
await pubsub.publish(EVENTS.COMMENT_ADDED, { commentAdded: comment });
return { comment };
},
},
};
Success Criteria:
- WebSocket connection established
- Subscriptions receive real-time updates
- Filtering works correctly
- Connection cleanup on disconnect
- Production-ready with Redis PubSub
Python Tools
schema_analyzer.py
Purpose: Analyze GraphQL schemas for quality, complexity, and best practices.
Usage:
# Basic analysis
python scripts/schema_analyzer.py schema.graphql
# JSON output for tooling
python scripts/schema_analyzer.py schema.graphql --output json
# Validate against best practices
python scripts/schema_analyzer.py schema.graphql --validate
# Analyze complexity and depth
python scripts/schema_analyzer.py schema.graphql --complexity
Features:
- Type system analysis (types, interfaces, unions, enums)
- Query/mutation/subscription inventory
- Complexity scoring per operation
- Deprecation tracking
- Naming convention validation
- Description coverage checking
- Circular reference detection
resolver_generator.py
Purpose: Generate TypeScript resolvers from GraphQL schema.
Usage:
# Generate resolvers
python scripts/resolver_generator.py schema.graphql --output src/resolvers
# With DataLoader integration
python scripts/resolver_generator.py schema.graphql --output src/resolvers --dataloader
# For specific types only
python scripts/resolver_generator.py schema.graphql --output src/resolvers --types User,Post
# Generate with tests
python scripts/resolver_generator.py schema.graphql --output src/resolvers --tests
Generated Output:
- Resolver files per type
- Type definitions
- DataLoader factories
- Context type definitions
- Jest test stubs
federation_scaffolder.py
Purpose: Scaffold Apollo Federation subgraphs with proper entity definitions.
Usage:
# Create new subgraph
python scripts/federation_scaffolder.py users-service --entities User,Profile
# With entity references
python scripts/federation_scaffolder.py posts-service --entities Post --references User
# Full service with Docker
python scripts/federation_scaffolder.py comments-service --entities Comment --docker --port 4003
# Scaffold gateway
python scripts/federation_scaffolder.py gateway --subgraphs users:4001,posts:4002,comments:4003
Generated Structure:
service-name/
├── src/
│ ├── schema.graphql # Federation schema
│ ├── resolvers/ # Type resolvers
│ ├── dataloaders/ # DataLoader factories
│ ├── datasources/ # Data access layer
│ └── index.ts # Apollo Server setup
├── tests/ # Jest tests
├── Dockerfile # Container definition
├── docker-compose.yml # Local development
└── package.json
Best Practices
Schema Design
- Use descriptive names (avoid abbreviations)
- Document all types and fields
- Implement Relay-style pagination for lists
- Use input types for mutations
- Return payload types from mutations (not raw types)
- Version breaking changes with new fields (not removal)
Resolver Patterns
- Keep resolvers thin (delegate to services)
- Use DataLoader for all batch-able relations
- Implement proper error handling
- Add authentication at resolver level
- Log slow resolvers for optimization
Federation
- Define clear subgraph boundaries
- Minimize cross-subgraph queries
- Use
@requires sparingly
- Implement proper health checks
- Version subgraph schemas independently
Performance
- Implement query complexity limits
- Use persisted queries in production
- Cache with appropriate TTLs
- Monitor resolver execution time
- Implement query depth limiting
References
Reference Files
references/schema-patterns.md - Schema design patterns and conventions
references/federation-guide.md - Apollo Federation architecture guide
references/performance-optimization.md - GraphQL performance best practices
External Resources
Version: 1.0.0
Last Updated: 2025-12-16
Skill Type: Engineering specialist
Python Tools: 3 (schema_analyzer.py, resolver_generator.py, federation_scaffolder.py)
Source: ForceInjection/domain-driven-design-skills — distributed by TomeVault.
1---2name: senior-graphql3description: GraphQL API design specialist for schema architecture, resolver patterns, federation, and performance optimization Use when this capability is needed.4---56# Senior GraphQL Specialist78Expert GraphQL API design and architecture skill for building scalable, type-safe APIs with Apollo Server, Federation, and modern GraphQL patterns.910## Overview1112This skill provides comprehensive GraphQL development capabilities including schema design, resolver implementation, federation architecture, real-time subscriptions, and performance optimization through DataLoader patterns.1314**Time Savings:** 50%+ reduction in GraphQL API development time through schema generation, resolver scaffolding, and automated federation setup.1516**Quality Improvement:** 40%+ improvement in API consistency through schema-first development, type safety enforcement, and automated best practices.1718## Core Capabilities1920### Schema Architecture21- Schema-first design methodology22- Type system design (scalars, enums, interfaces, unions)23- Input type and argument patterns24- Custom directive implementation25- Schema stitching and composition2627### Resolver Development28- Resolver pattern implementation29- Context and middleware integration30- Authentication/authorization in resolvers31- Error handling and formatting32- N+1 query prevention with DataLoader3334### Apollo Federation35- Supergraph architecture design36- Subgraph creation and entity definitions37- `@key`, `@external`, `@requires` directive usage38- Gateway configuration39- Schema composition validation4041### Performance Optimization42- Query complexity analysis and limiting43- Depth limiting implementation44- Caching strategies (Apollo Cache, Redis)45- Batching with DataLoader46- Persisted queries4748### Real-time Features49- Subscription implementation50- WebSocket configuration51- PubSub patterns52- Filtered subscriptions5354## Quick Start5556```bash57# Analyze existing GraphQL schema58python scripts/schema_analyzer.py schema.graphql --output json5960# Generate resolvers from schema61python scripts/resolver_generator.py schema.graphql --output src/resolvers6263# Scaffold Apollo Federation subgraph64python scripts/federation_scaffolder.py users-service --entities User,Profile65```6667## Key Workflows6869### 1. Schema-First API Design7071**Goal:** Design a type-safe GraphQL schema following best practices.7273**Steps:**74751. **Analyze Requirements**76 - Identify domain entities and relationships77 - Map CRUD operations to queries/mutations78 - Define subscription needs for real-time features79802. **Design Schema**81 ```graphql82 # Types with clear naming conventions83 type User {84 id: ID!85 email: String!86 profile: Profile87 posts(first: Int, after: String): PostConnection!88 createdAt: DateTime!89 }9091 # Relay-style pagination92 type PostConnection {93 edges: [PostEdge!]!94 pageInfo: PageInfo!95 totalCount: Int!96 }9798 type PostEdge {99 node: Post!100 cursor: String!101 }102103 type PageInfo {104 hasNextPage: Boolean!105 hasPreviousPage: Boolean!106 startCursor: String107 endCursor: String108 }109110 # Input types for mutations111 input CreateUserInput {112 email: String!113 name: String!114 password: String!115 }116117 # Clear query/mutation organization118 type Query {119 user(id: ID!): User120 users(first: Int, after: String): UserConnection!121 me: User122 }123124 type Mutation {125 createUser(input: CreateUserInput!): CreateUserPayload!126 updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!127 deleteUser(id: ID!): DeleteUserPayload!128 }129130 # Subscription for real-time131 type Subscription {132 userCreated: User!133 postPublished(authorId: ID): Post!134 }135 ```1361373. **Validate Schema**138 ```bash139 python scripts/schema_analyzer.py schema.graphql --validate140 ```1411424. **Generate Resolvers**143 ```bash144 python scripts/resolver_generator.py schema.graphql --output src/resolvers --typescript145 ```146147**Success Criteria:**148- Schema passes validation149- All types have descriptions150- Relay pagination implemented for lists151- Input types for all mutations152- Clear naming conventions followed153154### 2. DataLoader Implementation for N+1 Prevention155156**Goal:** Eliminate N+1 queries using DataLoader batching.157158**Problem Example:**159```graphql160# This query would cause N+1 without DataLoader161query {162 posts { # 1 query for posts163 author { # N queries for authors (one per post!)164 name165 }166 }167}168```169170**Solution:**1711721. **Create DataLoader Factory**173 ```typescript174 // src/dataloaders/index.ts175 import DataLoader from 'dataloader';176 import { prisma } from '../lib/prisma';177178 export const createLoaders = () => ({179 userLoader: new DataLoader<string, User>(async (userIds) => {180 const users = await prisma.user.findMany({181 where: { id: { in: [...userIds] } }182 });183 // Return in same order as requested IDs184 const userMap = new Map(users.map(u => [u.id, u]));185 return userIds.map(id => userMap.get(id) || null);186 }),187188 postsByAuthorLoader: new DataLoader<string, Post[]>(async (authorIds) => {189 const posts = await prisma.post.findMany({190 where: { authorId: { in: [...authorIds] } }191 });192 // Group posts by authorId193 const postMap = new Map<string, Post[]>();194 posts.forEach(post => {195 const existing = postMap.get(post.authorId) || [];196 existing.push(post);197 postMap.set(post.authorId, existing);198 });199 return authorIds.map(id => postMap.get(id) || []);200 }),201 });202203 export type Loaders = ReturnType<typeof createLoaders>;204 ```2052062. **Add Loaders to Context**207 ```typescript208 // src/server.ts209 import { createLoaders } from './dataloaders';210211 const server = new ApolloServer({212 typeDefs,213 resolvers,214 context: ({ req }) => ({215 user: authenticateToken(req),216 loaders: createLoaders(), // Fresh loaders per request217 }),218 });219 ```2202213. **Use in Resolvers**222 ```typescript223 // src/resolvers/post.resolver.ts224 export const PostResolvers = {225 Post: {226 author: (parent, _, { loaders }) => {227 return loaders.userLoader.load(parent.authorId);228 },229 },230 };231 ```2322334. **Verify Batching**234 - Enable query logging235 - Run test query236 - Confirm single batch query instead of N queries237238**Success Criteria:**239- Batch queries visible in logs240- Query count reduced from N+1 to 2241- Response time improved significantly242- DataLoader cache cleared per request243244### 3. Apollo Federation Setup245246**Goal:** Build a federated supergraph from multiple subgraphs.247248**Architecture:**249```250┌─────────────────────────────────────────────────┐251│ Apollo Gateway │252│ (Schema Composition) │253└─────────────────────────────────────────────────┘254 │ │ │255 ▼ ▼ ▼256┌─────────────┐ ┌─────────────┐ ┌─────────────┐257│ Users │ │ Posts │ │ Comments │258│ Subgraph │ │ Subgraph │ │ Subgraph │259└─────────────┘ └─────────────┘ └─────────────┘260```261262**Steps:**2632641. **Scaffold Subgraphs**265 ```bash266 # Create users subgraph267 python scripts/federation_scaffolder.py users-service \268 --entities User,Profile \269 --port 4001270271 # Create posts subgraph272 python scripts/federation_scaffolder.py posts-service \273 --entities Post \274 --references User \275 --port 4002276277 # Create comments subgraph278 python scripts/federation_scaffolder.py comments-service \279 --entities Comment \280 --references User,Post \281 --port 4003282 ```2832842. **Define Entity References**285 ```graphql286 # users-service/schema.graphql287 type User @key(fields: "id") {288 id: ID!289 email: String!290 name: String!291 profile: Profile292 }293294 # posts-service/schema.graphql295 type Post @key(fields: "id") {296 id: ID!297 title: String!298 content: String!299 author: User!300 }301302 # Extend User to add posts field303 extend type User @key(fields: "id") {304 id: ID! @external305 posts: [Post!]!306 }307308 # comments-service/schema.graphql309 type Comment @key(fields: "id") {310 id: ID!311 content: String!312 author: User!313 post: Post!314 }315316 extend type Post @key(fields: "id") {317 id: ID! @external318 comments: [Comment!]!319 }320 ```3213223. **Implement Reference Resolvers**323 ```typescript324 // posts-service/resolvers.ts325 export const resolvers = {326 User: {327 __resolveReference: async (user, { dataSources }) => {328 // Return only the fields this subgraph owns329 return { id: user.id };330 },331 posts: async (user, _, { dataSources }) => {332 return dataSources.postsAPI.getPostsByAuthor(user.id);333 },334 },335 Post: {336 __resolveReference: async (post, { dataSources }) => {337 return dataSources.postsAPI.getPost(post.id);338 },339 author: (post) => {340 // Return reference for gateway to resolve341 return { __typename: 'User', id: post.authorId };342 },343 },344 };345 ```3463474. **Configure Gateway**348 ```typescript349 // gateway/index.ts350 import { ApolloGateway, IntrospectAndCompose } from '@apollo/gateway';351 import { ApolloServer } from '@apollo/server';352353 const gateway = new ApolloGateway({354 supergraphSdl: new IntrospectAndCompose({355 subgraphs: [356 { name: 'users', url: 'http://localhost:4001/graphql' },357 { name: 'posts', url: 'http://localhost:4002/graphql' },358 { name: 'comments', url: 'http://localhost:4003/graphql' },359 ],360 }),361 });362363 const server = new ApolloServer({ gateway });364 ```3653665. **Test Federated Query**367 ```graphql368 query FederatedQuery {369 user(id: "123") {370 id371 name372 posts {373 id374 title375 comments {376 content377 author {378 name # Resolves back to users subgraph379 }380 }381 }382 }383 }384 ```385386**Success Criteria:**387- All subgraphs start without errors388- Schema composition succeeds389- Cross-subgraph queries resolve correctly390- Entity references work bidirectionally391392### 4. Real-time Subscriptions393394**Goal:** Implement GraphQL subscriptions for real-time updates.395396**Steps:**3973981. **Configure WebSocket Server**399 ```typescript400 // src/server.ts401 import { createServer } from 'http';402 import { WebSocketServer } from 'ws';403 import { useServer } from 'graphql-ws/lib/use/ws';404 import { ApolloServer } from '@apollo/server';405 import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';406407 const httpServer = createServer(app);408409 const wsServer = new WebSocketServer({410 server: httpServer,411 path: '/graphql',412 });413414 const serverCleanup = useServer(415 {416 schema,417 context: (ctx) => ({418 user: authenticateWebSocket(ctx.connectionParams),419 }),420 },421 wsServer422 );423424 const server = new ApolloServer({425 schema,426 plugins: [427 ApolloServerPluginDrainHttpServer({ httpServer }),428 {429 async serverWillStart() {430 return {431 async drainServer() {432 await serverCleanup.dispose();433 },434 };435 },436 },437 ],438 });439 ```4404412. **Implement PubSub**442 ```typescript443 // src/pubsub.ts444 import { PubSub } from 'graphql-subscriptions';445 import { RedisPubSub } from 'graphql-redis-subscriptions';446447 // For production, use Redis PubSub448 export const pubsub = new RedisPubSub({449 connection: process.env.REDIS_URL,450 });451452 // Event types453 export const EVENTS = {454 POST_CREATED: 'POST_CREATED',455 POST_UPDATED: 'POST_UPDATED',456 COMMENT_ADDED: 'COMMENT_ADDED',457 USER_ONLINE: 'USER_ONLINE',458 };459 ```4604613. **Define Subscription Schema**462 ```graphql463 type Subscription {464 postCreated: Post!465 postUpdated(id: ID!): Post!466 commentAdded(postId: ID!): Comment!467 userPresence(roomId: ID!): UserPresenceEvent!468 }469470 type UserPresenceEvent {471 user: User!472 status: PresenceStatus!473 }474475 enum PresenceStatus {476 ONLINE477 OFFLINE478 AWAY479 }480 ```4814824. **Implement Subscription Resolvers**483 ```typescript484 // src/resolvers/subscription.resolver.ts485 import { withFilter } from 'graphql-subscriptions';486 import { pubsub, EVENTS } from '../pubsub';487488 export const SubscriptionResolvers = {489 Subscription: {490 postCreated: {491 subscribe: () => pubsub.asyncIterator([EVENTS.POST_CREATED]),492 },493494 postUpdated: {495 subscribe: withFilter(496 () => pubsub.asyncIterator([EVENTS.POST_UPDATED]),497 (payload, variables) => {498 return payload.postUpdated.id === variables.id;499 }500 ),501 },502503 commentAdded: {504 subscribe: withFilter(505 () => pubsub.asyncIterator([EVENTS.COMMENT_ADDED]),506 (payload, variables, context) => {507 // Only notify if user has access to the post508 return payload.commentAdded.postId === variables.postId;509 }510 ),511 },512 },513 };514 ```5155165. **Publish Events**517 ```typescript518 // src/resolvers/mutation.resolver.ts519 export const MutationResolvers = {520 Mutation: {521 createPost: async (_, { input }, { user, prisma }) => {522 const post = await prisma.post.create({523 data: { ...input, authorId: user.id },524 });525526 // Publish to subscribers527 await pubsub.publish(EVENTS.POST_CREATED, { postCreated: post });528529 return { post };530 },531532 addComment: async (_, { input }, { user, prisma }) => {533 const comment = await prisma.comment.create({534 data: { ...input, authorId: user.id },535 });536537 // Publish to subscribers watching this post538 await pubsub.publish(EVENTS.COMMENT_ADDED, { commentAdded: comment });539540 return { comment };541 },542 },543 };544 ```545546**Success Criteria:**547- WebSocket connection established548- Subscriptions receive real-time updates549- Filtering works correctly550- Connection cleanup on disconnect551- Production-ready with Redis PubSub552553## Python Tools554555### schema_analyzer.py556557**Purpose:** Analyze GraphQL schemas for quality, complexity, and best practices.558559**Usage:**560```bash561# Basic analysis562python scripts/schema_analyzer.py schema.graphql563564# JSON output for tooling565python scripts/schema_analyzer.py schema.graphql --output json566567# Validate against best practices568python scripts/schema_analyzer.py schema.graphql --validate569570# Analyze complexity and depth571python scripts/schema_analyzer.py schema.graphql --complexity572```573574**Features:**575- Type system analysis (types, interfaces, unions, enums)576- Query/mutation/subscription inventory577- Complexity scoring per operation578- Deprecation tracking579- Naming convention validation580- Description coverage checking581- Circular reference detection582583### resolver_generator.py584585**Purpose:** Generate TypeScript resolvers from GraphQL schema.586587**Usage:**588```bash589# Generate resolvers590python scripts/resolver_generator.py schema.graphql --output src/resolvers591592# With DataLoader integration593python scripts/resolver_generator.py schema.graphql --output src/resolvers --dataloader594595# For specific types only596python scripts/resolver_generator.py schema.graphql --output src/resolvers --types User,Post597598# Generate with tests599python scripts/resolver_generator.py schema.graphql --output src/resolvers --tests600```601602**Generated Output:**603- Resolver files per type604- Type definitions605- DataLoader factories606- Context type definitions607- Jest test stubs608609### federation_scaffolder.py610611**Purpose:** Scaffold Apollo Federation subgraphs with proper entity definitions.612613**Usage:**614```bash615# Create new subgraph616python scripts/federation_scaffolder.py users-service --entities User,Profile617618# With entity references619python scripts/federation_scaffolder.py posts-service --entities Post --references User620621# Full service with Docker622python scripts/federation_scaffolder.py comments-service --entities Comment --docker --port 4003623624# Scaffold gateway625python scripts/federation_scaffolder.py gateway --subgraphs users:4001,posts:4002,comments:4003626```627628**Generated Structure:**629```630service-name/631├── src/632│ ├── schema.graphql # Federation schema633│ ├── resolvers/ # Type resolvers634│ ├── dataloaders/ # DataLoader factories635│ ├── datasources/ # Data access layer636│ └── index.ts # Apollo Server setup637├── tests/ # Jest tests638├── Dockerfile # Container definition639├── docker-compose.yml # Local development640└── package.json641```642643## Best Practices644645### Schema Design646- Use descriptive names (avoid abbreviations)647- Document all types and fields648- Implement Relay-style pagination for lists649- Use input types for mutations650- Return payload types from mutations (not raw types)651- Version breaking changes with new fields (not removal)652653### Resolver Patterns654- Keep resolvers thin (delegate to services)655- Use DataLoader for all batch-able relations656- Implement proper error handling657- Add authentication at resolver level658- Log slow resolvers for optimization659660### Federation661- Define clear subgraph boundaries662- Minimize cross-subgraph queries663- Use `@requires` sparingly664- Implement proper health checks665- Version subgraph schemas independently666667### Performance668- Implement query complexity limits669- Use persisted queries in production670- Cache with appropriate TTLs671- Monitor resolver execution time672- Implement query depth limiting673674## References675676### Reference Files677- `references/schema-patterns.md` - Schema design patterns and conventions678- `references/federation-guide.md` - Apollo Federation architecture guide679- `references/performance-optimization.md` - GraphQL performance best practices680681### External Resources682- [GraphQL Specification](https://spec.graphql.org/)683- [Apollo Server Documentation](https://www.apollographql.com/docs/apollo-server/)684- [Apollo Federation](https://www.apollographql.com/docs/federation/)685- [DataLoader](https://github.com/graphql/dataloader)686687---688689**Version:** 1.0.0690**Last Updated:** 2025-12-16691**Skill Type:** Engineering specialist692**Python Tools:** 3 (schema_analyzer.py, resolver_generator.py, federation_scaffolder.py)693694---695> Source: [ForceInjection/domain-driven-design-skills](https://github.com/ForceInjection/domain-driven-design-skills) — distributed by [TomeVault](https://tomevault.io).696<!-- tomevault:4.0:skill_md:2026-06-15 -->