GraphQL Schema Generator Skill
Expert at creating GraphQL schemas with proper types, resolvers, and best practices.
When to Activate
-
"create GraphQL schema for [entity]"
-
"generate GraphQL API"
-
"build GraphQL types and resolvers"
Complete GraphQL Structure
// schema/user.schema.ts import { gql } from 'apollo-server-express';
export const userTypeDefs = gql` type User { id: ID! email: String! name: String! role: UserRole! posts: [Post!]! createdAt: DateTime! updatedAt: DateTime! }
enum UserRole { USER ADMIN MODERATOR }
input CreateUserInput { email: String! name: String! password: String! role: UserRole = USER }
input UpdateUserInput { email: String name: String password: String role: UserRole }
type UserConnection { edges: [UserEdge!]! pageInfo: PageInfo! totalCount: Int! }
type UserEdge { node: User! cursor: String! }
type Query { user(id: ID!): User users( first: Int = 10 after: String search: String ): UserConnection! me: User }
type Mutation { createUser(input: CreateUserInput!): User! updateUser(id: ID!, input: UpdateUserInput!): User! deleteUser(id: ID!): Boolean! }
type Subscription { userCreated: User! userUpdated(id: ID!): User! } `;
// resolvers/user.resolvers.ts import { UserInputError, AuthenticationError } from 'apollo-server-express'; import { UserService } from '../services/user.service'; import { pubsub } from '../pubsub';
const USER_CREATED = 'USER_CREATED'; const USER_UPDATED = 'USER_UPDATED';
export const userResolvers = { Query: { user: async (_parent, { id }, { services, user }) => { if (!user) { throw new AuthenticationError('Not authenticated'); }
return await services.user.findById(id);
},
users: async (_parent, { first, after, search }, { services, user }) => {
if (!user) {
throw new AuthenticationError('Not authenticated');
}
const result = await services.user.findAll({
first,
after,
search,
});
return {
edges: result.users.map(user => ({
node: user,
cursor: Buffer.from(user.id.toString()).toString('base64'),
})),
pageInfo: {
hasNextPage: result.hasNextPage,
endCursor: result.endCursor,
},
totalCount: result.totalCount,
};
},
me: async (_parent, _args, { user }) => {
if (!user) {
throw new AuthenticationError('Not authenticated');
}
return user;
},
},
Mutation: { createUser: async (_parent, { input }, { services }) => { const user = await services.user.create(input);
// Publish subscription event
pubsub.publish(USER_CREATED, { userCreated: user });
return user;
},
updateUser: async (_parent, { id, input }, { services, user }) => {
if (!user) {
throw new AuthenticationError('Not authenticated');
}
if (user.id !== id && user.role !== 'ADMIN') {
throw new AuthenticationError('Not authorized');
}
const updatedUser = await services.user.update(id, input);
if (!updatedUser) {
throw new UserInputError('User not found');
}
// Publish subscription event
pubsub.publish(USER_UPDATED, {
userUpdated: updatedUser,
id,
});
return updatedUser;
},
deleteUser: async (_parent, { id }, { services, user }) => {
if (!user || user.role !== 'ADMIN') {
throw new AuthenticationError('Admin access required');
}
const success = await services.user.delete(id);
if (!success) {
throw new UserInputError('User not found');
}
return true;
},
},
Subscription: { userCreated: { subscribe: () => pubsub.asyncIterator([USER_CREATED]), },
userUpdated: {
subscribe: withFilter(
() => pubsub.asyncIterator([USER_UPDATED]),
(payload, variables) => {
return payload.id === variables.id;
}
),
},
},
User: { // Field resolver for nested data posts: async (parent, _args, { services }) => { return await services.post.findByAuthorId(parent.id); }, }, };
DataLoader Pattern
// dataloaders/user.dataloader.ts import DataLoader from 'dataloader'; import { UserService } from '../services/user.service';
export function createUserLoader(userService: UserService) { return new DataLoader(async (ids: readonly string[]) => { const users = await userService.findByIds([...ids]);
const userMap = new Map(users.map(user => [user.id, user]));
return ids.map(id => userMap.get(id) || null);
}); }
// Use in context export const createContext = ({ req }) => { const userService = new UserService();
return { user: req.user, services: { user: userService, }, loaders: { user: createUserLoader(userService), }, }; };
Best Practices
-
Use clear, descriptive type names
-
Implement pagination (Connection pattern)
-
Add input validation
-
Use enums for fixed values
-
Implement authentication/authorization
-
Use DataLoaders to prevent N+1 queries
-
Add proper error handling
-
Document schema with descriptions
-
Version your API
-
Use subscriptions for real-time data
-
Implement field-level resolvers
-
Cache responses when appropriate
Output Checklist
-
✅ Type definitions created
-
✅ Resolvers implemented
-
✅ Queries/Mutations/Subscriptions
-
✅ DataLoaders setup
-
✅ Authentication added
-
✅ Error handling
-
📝 Usage examples