Skip to main content Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/pluginagentmarketplace/custom-plugin-api-design --skill graphql명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
| name | graphql |
| version | 2.0.0 |
| description | GraphQL API design and schema development |
| sasmp_version | 1.3.0 |
| bonded_agent | 01-api-architect |
| bond_type | PRIMARY_BOND |
| atomic_design | {"single_responsibility":"GraphQL schema design and resolver patterns","boundaries":{"includes":["schema_design","resolvers","subscriptions","n_plus_one","caching"],"excludes":["rest_design","database_queries","frontend_code"]}} |
| parameter_validation | {"schema":{"type":"object","properties":{"operation_type":{"type":"string","enum":["query","mutation","subscription"]},"complexity_limit":{"type":"number","minimum":0,"maximum":1000}}}} |
| retry_config | {"enabled":true,"max_attempts":3,"backoff":{"type":"exponential","initial_delay_ms":1000,"max_delay_ms":30000}} |
| logging | {"level":"INFO","fields":["operation_name","complexity","duration_ms"]} |
| dependencies | {"skills":["api-architecture"],"agents":["01-api-architect"]} |
GraphQL API Design Skill
Purpose
Design efficient GraphQL APIs with proper schema patterns.
Schema Design
Types
scalar DateTime
scalar UUID
scalar Email
type User {
id: ID!
email: Email!
name: String!
status: UserStatus!
profile: Profile
teams: [Team!]!
createdAt: DateTime!
updatedAt: DateTime
}
enum UserStatus {
ACTIVE
INACTIVE
BANNED
}
interface Node {
id: ID!
}
union SearchResult = User | Team | Post
Queries
type
user ID User
users
Int
String
Int
String
UserFilter
UserConnection
search String, SearchType SearchResult
UserFilter
UserStatus
String
DateTime
Query
{
(
id
:
!
)
:
(
first
:
after
:
last
:
before
:
filter
:
)
:
!
(
query
:
!
types
:
[
!
]
)
:
[
!
]
!
}
input
{
status
:
role
:
createdAfter
:
}
Mutations
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
verifyUser(id: ID!): VerifyUserPayload!
}
input CreateUserInput {
email: Email!
name: String!
password: String!
}
input UpdateUserInput {
name: String
status: UserStatus
}
type CreateUserPayload {
user: User
errors: [UserError!]!
}
type UserError {
field: String
message: String!
code: ErrorCode!
}
Subscriptions
type Subscription {
userCreated: User!
userUpdated(id: ID): User!
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: {
teams: async (user, _, context) => {
return context.dataSources.teams.findByUserId(user.id);
},
},
};
DataLoader (N+1 Solution)
import DataLoader from 'dataloader';
const userLoader = new DataLoader(async (ids: string[]) => {
const users = await db.query(
'SELECT * FROM users WHERE id = ANY($1)',
[ids]
);
const userMap = new Map(users.map(u => [u.id, u]));
return ids.map(id => userMap.get(id) || null);
});
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';
throw new GraphQLError('User not found', {
extensions: {
code: 'NOT_FOUND',
field: 'userId',
},
});
const server = new ApolloServer({
formatError: (error) => {
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