Agent skill
graphql
GraphQL API design and schema development
Install this agent skill to your Project
npx add-skill https://github.com/pluginagentmarketplace/custom-plugin-api-design/tree/main/skills/graphql
SKILL.md
GraphQL API Design Skill
Purpose
Design efficient GraphQL APIs with proper schema patterns.
Schema Design
Types
# Scalar types
scalar DateTime
scalar UUID
scalar Email
# Object type
type User {
id: ID!
email: Email!
name: String!
status: UserStatus!
profile: Profile
teams: [Team!]!
createdAt: DateTime!
updatedAt: DateTime
}
# Enum
enum UserStatus {
ACTIVE
INACTIVE
BANNED
}
# Interface
interface Node {
id: ID!
}
# Union
union SearchResult = User | Team | Post
Queries
type Query {
# Single resource
user(id: ID!): User
# Paginated list (Relay-style)
users(
first: Int
after: String
last: Int
before: String
filter: UserFilter
): UserConnection!
# Search
search(query: String!, types: [SearchType!]): [SearchResult!]!
}
# Filter input
input UserFilter {
status: UserStatus
role: String
createdAfter: DateTime
}
Mutations
type Mutation {
# Create
createUser(input: CreateUserInput!): CreateUserPayload!
# Update
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
# Delete
deleteUser(id: ID!): DeleteUserPayload!
# Action
verifyUser(id: ID!): VerifyUserPayload!
}
# Input types
input CreateUserInput {
email: Email!
name: String!
password: String!
}
input UpdateUserInput {
name: String
status: UserStatus
}
# Payload types (with errors)
type CreateUserPayload {
user: User
errors: [UserError!]!
}
type UserError {
field: String
message: String!
code: ErrorCode!
}
Subscriptions
type Subscription {
# Real-time updates
userCreated: User!
userUpdated(id: ID): User!
# Filtered subscription
orderStatusChanged(orderId: ID!): Order!
}
Connection Pattern (Relay)
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
Resolver Patterns
Basic Resolver
const resolvers = {
Query: {
user: async (_, { id }, context) => {
return context.dataSources.users.findById(id);
},
users: async (_, { first, after, filter }, context) => {
return context.dataSources.users.findMany({
first,
after,
filter,
});
},
},
User: {
// Field resolver
teams: async (user, _, context) => {
return context.dataSources.teams.findByUserId(user.id);
},
},
};
DataLoader (N+1 Solution)
import DataLoader from 'dataloader';
// Create loader
const userLoader = new DataLoader(async (ids: string[]) => {
const users = await db.query(
'SELECT * FROM users WHERE id = ANY($1)',
[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);
});
// Use in resolver
const resolvers = {
Post: {
author: (post, _, context) => {
return context.loaders.user.load(post.authorId);
},
},
};
Context Setup
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
user: req.user,
loaders: {
user: new DataLoader(batchUsers),
team: new DataLoader(batchTeams),
},
dataSources: {
users: new UserDataSource(db),
teams: new TeamDataSource(db),
},
}),
});
Error Handling
import { GraphQLError } from 'graphql';
// Custom error
throw new GraphQLError('User not found', {
extensions: {
code: 'NOT_FOUND',
field: 'userId',
},
});
// Error formatting
const server = new ApolloServer({
formatError: (error) => {
// Log internal errors
if (error.extensions?.code === 'INTERNAL_SERVER_ERROR') {
logger.error(error);
return { message: 'Internal server error' };
}
return error;
},
});
Security
Query Complexity
import { createComplexityRule } from 'graphql-query-complexity';
const complexityRule = createComplexityRule({
maximumComplexity: 1000,
estimators: [
fieldExtensionsEstimator(),
simpleEstimator({ defaultComplexity: 1 }),
],
onComplete: (complexity) => {
console.log('Query complexity:', complexity);
},
});
const server = new ApolloServer({
validationRules: [complexityRule],
});
Depth Limiting
import depthLimit from 'graphql-depth-limit';
const server = new ApolloServer({
validationRules: [depthLimit(10)],
});
Unit Test Template
import { describe, it, expect } from 'vitest';
import { ApolloServer } from '@apollo/server';
import { typeDefs, resolvers } from './schema';
describe('GraphQL API', () => {
const server = new ApolloServer({ typeDefs, resolvers });
describe('Query.user', () => {
it('should return user by id', async () => {
const result = await server.executeOperation({
query: `
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`,
variables: { id: 'user-123' },
});
expect(result.body.singleResult.data?.user).toEqual({
id: 'user-123',
name: 'John Doe',
email: 'john@example.com',
});
});
it('should return null for non-existent user', async () => {
const result = await server.executeOperation({
query: `query { user(id: "invalid") { id } }`,
});
expect(result.body.singleResult.data?.user).toBeNull();
});
});
describe('Mutation.createUser', () => {
it('should create user and return payload', async () => {
const result = await server.executeOperation({
query: `
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
user { id name }
errors { field message }
}
}
`,
variables: {
input: { email: 'new@example.com', name: 'New User', password: 'Secret123!' },
},
});
expect(result.body.singleResult.data?.createUser.user).toBeDefined();
expect(result.body.singleResult.data?.createUser.errors).toEqual([]);
});
});
});
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| N+1 queries | Field-level resolvers | Use DataLoader |
| Slow queries | High complexity | Add complexity limits |
| Memory issues | Large result sets | Implement pagination |
| Introspection leak | Enabled in production | Disable in prod |
Quality Checklist
- Schema follows naming conventions
- Relay connection pattern for lists
- Input/Payload types for mutations
- DataLoader for relationships
- Query complexity limits set
- Depth limiting enabled
- Introspection disabled in production
- Error handling standardized
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
versioning
API versioning strategies and backward compatibility
frontend-patterns
Frontend development and API integration patterns for React, TypeScript, and state management
rest
RESTful API design principles and best practices
testing
API testing strategies and contract testing
documentation
API documentation with OpenAPI and developer portals
database-patterns
Database design, optimization, and caching strategies for SQL, NoSQL, and Redis
Didn't find tool you were looking for?