GraphQL API design and schema development
Designs efficient GraphQL APIs with proper schema patterns, resolvers, and security controls. Triggers when creating GraphQL schemas, designing queries/mutations, or solving N+1 problems with DataLoader.
/plugin marketplace add pluginagentmarketplace/custom-plugin-api-design/plugin install custom-plugin-api-design@pluginagentmarketplace-api-designThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/config.yamlassets/schema.jsonreferences/GUIDE.mdreferences/PATTERNS.mdscripts/validate.pyDesign efficient GraphQL APIs with proper schema patterns.
# 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
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
}
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!
}
type Subscription {
# Real-time updates
userCreated: User!
userUpdated(id: ID): User!
# Filtered subscription
orderStatusChanged(orderId: ID!): Order!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
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);
},
},
};
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);
},
},
};
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),
},
}),
});
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;
},
});
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],
});
import depthLimit from 'graphql-depth-limit';
const server = new ApolloServer({
validationRules: [depthLimit(10)],
});
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([]);
});
});
});
| 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 |
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.