| name | graphql |
| description | GraphQL schema design, resolvers, directives, subscriptions, and best practices for API development. |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep |
GraphQL Skill
Expert assistance for designing and implementing GraphQL APIs.
Capabilities
- Design GraphQL schemas with SDL
- Implement resolvers and data loaders
- Create custom directives
- Set up subscriptions for real-time
- Handle authentication and authorization
- Optimize query performance
Usage
Invoke this skill when you need to:
- Design GraphQL API schemas
- Implement resolvers
- Add real-time subscriptions
- Create custom directives
- Optimize N+1 queries
Inputs
| Parameter | Type | Required | Description |
|---|
| typeName | string | Yes | GraphQL type name |
| operations | array | No | queries, mutations, subscriptions |
| directives | array | No | Custom directives |
Schema Design Patterns
Type Definitions
scalar DateTime
scalar JSON
enum Role {
USER
ADMIN
}
enum SortOrder {
ASC
DESC
}
type User {
id: ID!
name: String!
email: String!
role: Role!
posts: [Post!]!
createdAt: DateTime!
updatedAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
published: Boolean!
author: User!
comments: [Comment!]!
createdAt: DateTime!
}
type Comment {
id: ID!
content: String!
author: User!
post: Post!
createdAt: DateTime!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type UserEdge {
node: User!
cursor: String!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
input CreateUserInput {
name: String!
email: String!
password: String!
role: Role = USER
}
input UpdateUserInput {
name: String
email: String
role: Role
}
input UsersFilterInput {
search: String
role: Role
}
input PaginationInput {
first: Int
after: String
last: Int
before: String
}
type Query {
me: User
user(id: ID!): User
users(
filter: UsersFilterInput
pagination: PaginationInput
orderBy: SortOrder
): UserConnection!
post(id: ID!): Post
posts(published: Boolean): [Post!]!
}
type Mutation {
login(email: String!, password: String!): AuthPayload!
register(input: CreateUserInput!): AuthPayload!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createPost(title: String!, content: String!): Post!
updatePost(id: ID!, title: String, content: String, published: Boolean): Post!
deletePost(id: ID!): Boolean!
}
type Subscription {
postCreated: Post!
postUpdated(id: ID!): Post!
commentAdded(postId: ID!): Comment!
}
type AuthPayload {
token: String!
user: User!
}
directive @auth(requires: Role = USER) on FIELD_DEFINITION
directive @deprecated(reason: String) on FIELD_DEFINITION
Resolvers
import { Resolvers } from '../generated/graphql';
import { Context } from '../context';
import { userResolvers } from './user.resolvers';
import { postResolvers } from './post.resolvers';
import { authResolvers } from './auth.resolvers';
export const resolvers: Resolvers<Context> = {
Query: {
...userResolvers.Query,
...postResolvers.Query,
},
Mutation: {
...authResolvers.Mutation,
...userResolvers.Mutation,
...postResolvers.Mutation,
},
Subscription: {
...postResolvers.Subscription,
},
User: userResolvers.User,
Post: postResolvers.Post,
};
import { GraphQLError } from 'graphql';
import { Context } from '../context';
export const userResolvers = {
Query: {
me: (: , : , { user, prisma }: ) => {
(!user) ();
prisma..({ : { : user. } });
},
: (: , { id }: { : }, { prisma }: ) => {
prisma..({ : { id } });
},
: (
: ,
{ filter, pagination, orderBy }: ,
{ prisma }:
) => {
{ first = , after } = pagination || {};
where = filter?.
? { : { : filter., : } }
: ;
users = prisma..({
where,
: first + ,
: after ? { : after } : ,
: after ? : ,
: { : orderBy || },
});
hasNextPage = users. > first;
edges = users.(, first).( ({
: user,
: user.,
}));
{
edges,
: {
hasNextPage,
: !!after,
: edges[]?.,
: edges[edges. - ]?.,
},
: prisma..({ where }),
};
},
},
: {
: (
: ,
{ id, input }: { : ; : },
{ user, prisma }:
) => {
(!user) ();
(user. !== id && user. !== ) {
();
}
prisma..({
: { id },
: input,
});
},
},
: {
: (: , : , { prisma }: ) => {
prisma..({ : { : parent. } });
},
},
};
DataLoader for N+1
import DataLoader from 'dataloader';
import { PrismaClient, User, Post } from '@prisma/client';
export function createLoaders(prisma: PrismaClient) {
return {
userLoader: new DataLoader<string, User | null>(async (ids) => {
const users = await prisma.user.findMany({
where: { id: { in: ids as string[] } },
});
const userMap = new Map(users.map((u) => [u.id, u]));
return ids.map((id) => userMap.get(id) || null);
}),
postsByAuthorLoader: new DataLoader<string, Post[]>(async (authorIds) => {
posts = prisma..({
: { : { : authorIds [] } },
});
postsByAuthor = <, []>();
posts.( {
authorPosts = postsByAuthor.(post.) || [];
authorPosts.(post);
postsByAuthor.(post., authorPosts);
});
authorIds.( postsByAuthor.(id) || []);
}),
};
}
: {
: loaders..(parent.),
}
Best Practices
- Use input types for mutations
- Implement cursor-based pagination
- Use DataLoader for N+1 prevention
- Add proper error handling
- Document schema with descriptions
Target Processes
- graphql-api-development
- api-design
- backend-development
- real-time-applications