graphql-expert
Included with Lifetime
$97 forever
Expert-level GraphQL API development with schema design, resolvers, and subscriptions
apigraphqlapiapolloschemaresolverssubscriptionsrelay
What this skill does
# GraphQL Expert
Expert guidance for GraphQL API development, schema design, resolvers, subscriptions, and best practices for building type-safe, efficient APIs.
## Core Concepts
### Schema Design
- Type system and schema definition language (SDL)
- Object types, interfaces, unions, and enums
- Input types and custom scalars
- Schema stitching and federation
- Modular schema organization
### Resolvers
- Resolver functions and data sources
- Context and info arguments
- Field-level resolvers
- Resolver chains and data loaders
- Error handling in resolvers
### Queries and Mutations
- Query design and naming conventions
- Mutation patterns and best practices
- Input validation and sanitization
- Pagination strategies (cursor-based, offset)
- Filtering and sorting
### Subscriptions
- Real-time updates with WebSocket
- Subscription resolvers
- PubSub patterns
- Subscription filtering
- Connection management
### Performance
- N+1 query problem and DataLoader
- Query complexity analysis
- Depth limiting and query cost
- Caching strategies (field-level, full response)
- Batching and deduplication
## Modern GraphQL Development
### Apollo Server 4
```typescript
// Apollo Server 4 setup
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
// Type definitions
const typeDefs = `#graphql
type User {
id: ID!
email: String!
name: String!
posts: [Post!]!
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
published: Boolean!
tags: [String!]!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
users(limit: Int = 10, offset: Int = 0): UsersConnection!
user(id: ID!): User
posts(filter: PostFilter, sort: SortOrder): [Post!]!
post(id: ID!): Post
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createPost(input: CreatePostInput!): Post!
updatePost(id: ID!, input: UpdatePostInput!): Post!
publishPost(id: ID!): Post!
}
type Subscription {
postPublished: Post!
userCreated: User!
}
input CreateUserInput {
email: String!
name: String!
password: String!
}
input UpdateUserInput {
email: String
name: String
}
input CreatePostInput {
title: String!
content: String!
tags: [String!]
}
input UpdatePostInput {
title: String
content: String
tags: [String!]
}
input PostFilter {
published: Boolean
authorId: ID
tag: String
}
type UsersConnection {
nodes: [User!]!
totalCount: Int!
pageInfo: PageInfo!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
}
enum SortOrder {
NEWEST_FIRST
OLDEST_FIRST
TITLE_ASC
TITLE_DESC
}
scalar DateTime
`;
// Resolvers
const resolvers = {
Query: {
users: async (_, { limit, offset }, { dataSources }) => {
const users = await dataSources.userAPI.getUsers({ limit, offset });
const totalCount = await dataSources.userAPI.getTotalCount();
return {
nodes: users,
totalCount,
pageInfo: {
hasNextPage: offset + limit < totalCount,
hasPreviousPage: offset > 0,
},
};
},
user: async (_, { id }, { dataSources }) => {
return dataSources.userAPI.getUserById(id);
},
posts: async (_, { filter, sort }, { dataSources }) => {
return dataSources.postAPI.getPosts({ filter, sort });
},
post: async (_, { id }, { dataSources }) => {
return dataSources.postAPI.getPostById(id);
},
},
Mutation: {
createUser: async (_, { input }, { dataSources, user }) => {
// Validate input
if (!isValidEmail(input.email)) {
throw new GraphQLError('Invalid email address', {
extensions: { code: 'BAD_USER_INPUT' },
});
}
return dataSources.userAPI.createUser(input);
},
updateUser: async (_, { id, input }, { dataSources, user }) => {
// Check authorization
if (user.id !== id && !user.isAdmin) {
throw new GraphQLError('Not authorized', {
extensions: { code: 'FORBIDDEN' },
});
}
return dataSources.userAPI.updateUser(id, input);
},
createPost: async (_, { input }, { dataSources, user, pubsub }) => {
if (!user) {
throw new GraphQLError('Not authenticated', {
extensions: { code: 'UNAUTHENTICATED' },
});
}
const post = await dataSources.postAPI.createPost({
...input,
authorId: user.id,
});
return post;
},
publishPost: async (_, { id }, { dataSources, user, pubsub }) => {
const post = await dataSources.postAPI.publishPost(id);
// Trigger subscription
pubsub.publish('POST_PUBLISHED', { postPublished: post });
return post;
},
},
Subscription: {
postPublished: {
subscribe: (_, __, { pubsub }) => pubsub.asyncIterator(['POST_PUBLISHED']),
},
userCreated: {
subscribe: (_, __, { pubsub }) => pubsub.asyncIterator(['USER_CREATED']),
},
},
User: {
posts: async (parent, _, { dataSources }) => {
return dataSources.postAPI.getPostsByAuthorId(parent.id);
},
},
Post: {
author: async (parent, _, { dataSources }) => {
return dataSources.userAPI.getUserById(parent.authorId);
},
},
DateTime: new GraphQLScalarType({
name: 'DateTime',
description: 'ISO 8601 date-time string',
serialize(value: Date) {
return value.toISOString();
},
parseValue(value: string) {
return new Date(value);
},
parseLiteral(ast) {
if (ast.kind === Kind.STRING) {
return new Date(ast.value);
}
return null;
},
}),
};
// Server setup
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
],
});
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => {
const token = req.headers.authorization || '';
const user = await getUserFromToken(token);
return {
user,
dataSources: {
userAPI: new UserAPI(),
postAPI: new PostAPI(),
},
pubsub,
};
},
listen: { port: 4000 },
});
```
### DataLoader for N+1 Prevention
```typescript
import DataLoader from 'dataloader';
// Create DataLoaders
class UserAPI {
private loader: DataLoader<string, User>;
constructor() {
this.loader = new DataLoader(async (ids: readonly string[]) => {
// Batch fetch users
const users = await db.user.findMany({
where: { id: { in: [...ids] } },
});
// Return in same order as input ids
const userMap = new Map(users.map(u => [u.id, u]));
return ids.map(id => userMap.get(id) ?? null);
});
}
async getUserById(id: string): Promise<User | null> {
return this.loader.load(id);
}
async getUsersByIds(ids: string[]): Promise<(User | null)[]> {
return this.loader.loadMany(ids);
}
}
// Usage in resolvers
const resolvers = {
Post: {
author: async (parent, _, { dataSources }) => {
// This will be batched with DataLoader
return dataSources.userAPI.getUserById(parent.authorId);
},
},
};
```
### GraphQL Codegen
```yaml
# codegen.yml
schema: './src/schema.graphql'
documents: './src/**/*.graphql'
generates:
src/generated/graphql.ts:
plugins:
- typescript
- typescript-resolvers
- typescript-operations
config:
useIndexSignature: true
contextType: '../context#Context'
mappers:
User: '../models#UserModel'
Post: '../models#PostModel'
```
```typescript
// Generated types usage
import { Resolvers } from './generated/graphql';
const resRelated in api
microservices-expert
IncludedExpert-level microservices architecture, patterns, service mesh, and distributed systems
api
api-design-expert
IncludedExpert-level API design principles, REST, GraphQL, versioning, and API best practices
api
openapi-expert
IncludedExpert-level OpenAPI/Swagger specification for API design, documentation, and code generation
api
grpc-expert
IncludedExpert-level gRPC, Protocol Buffers, microservices communication, and streaming
api
fastapi-expert
IncludedExpert-level FastAPI development for high-performance Python APIs with async support
api