본문으로 건너뛰기
cccskills
무료GitHub에 공개

graphql-design

GraphQL schema design, resolver patterns, subscriptions, DataLoader for N+1 prevention, and error handling

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md4.9 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

GraphQL Design

Schema Design

type Query {
  user(id: ID!): User
  users(filter: UserFilter, first: Int = 20, after: String): UserConnection!
}

type Mutation {
  createUser(input: CreateUserInput!): CreateUserPayload!
  updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
}

type Subscription {
  orderStatusChanged(orderId: ID!): Order!
}

type User {
  id: ID!
  email: String!
  name: String!
  orders(first: Int = 10, after: String): OrderConnection!
  createdAt: DateTime!
}

input CreateUserInput {
  email: String!
  name: String!
}

type CreateUserPayload {
  user: User
  errors: [UserError!]!
}

type UserError {
  field: String!
  message: String!
}

type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type UserEdge {
  node: User!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  endCursor: String
}

Use Relay-style connections for pagination. Return payload types from mutations with both result and errors.

Resolvers

const resolvers: Resolvers = {
  Query: {
    user: async (_, { id }, ctx) => {
      return ctx.dataloaders.user.load(id);
    },
    users: async (_, { filter, first, after }, ctx) => {
      const cursor = after ? decodeCursor(after) : undefined;
      const users = await ctx.db.user.findMany({
        where: buildFilter(filter),
        take: first + 1,
        cursor: cursor ? { id: cursor } : undefined,
        orderBy: { createdAt: "desc" },
      });

      const hasNextPage = users.length > first;
      const edges = users.slice(0, first).map(user => ({
        node: user,
        cursor: encodeCursor(user.id),
      }));

      return {
        edges,
        pageInfo: {
          hasNextPage,
          endCursor: edges[edges.length - 1]?.cursor ?? null,
        },
      };
    },
  },

  Mutation: {
    createUser: async (_, { input }, ctx) => {
      const existing = await ctx.db.user.findUnique({ where: { email: input.email } });
      if (existing) {
        return { user: null, errors: [{ field: "email", message: "Already taken" }] };
      }
      const user = await ctx.db.user.create({ data: input });
      return { user, errors: [] };
    },
  },

  User: {
    orders: async (parent, { first, after }, ctx) => {
      return ctx.dataloaders.userOrders.load({ userId: parent.id, first, after });
    },
  },
};

DataLoader for N+1 Prevention

import DataLoader from "dataloader";

function createLoaders(db: Database) {
  return {
    user: new DataLoader<string, User>(async (ids) => {
      const users = await db.user.findMany({ where: { id: { in: [...ids] } } });
      const userMap = new Map(users.map(u => [u.id, u]));
      return ids.map(id => userMap.get(id) ?? new Error(`User ${id} not found`));
    }),

    userOrders: new DataLoader<{ userId: string }, Order[]>(async (keys) => {
      const userIds = keys.map(k => k.userId);
      const orders = await db.order.findMany({
        where: { userId: { in: userIds } },
        orderBy: { createdAt: "desc" },
      });
      const grouped = new Map<string, Order[]>();
      orders.forEach(o => {
        const list = grouped.get(o.userId) ?? [];
        list.push(o);
        grouped.set(o.userId, list);
      });
      return keys.map(k => grouped.get(k.userId) ?? []);
    }),
  };
}

Create new DataLoader instances per request to avoid stale cache across users.

Subscriptions

const pubsub = new PubSub();

const resolvers = {
  Subscription: {
    orderStatusChanged: {
      subscribe: (_, { orderId }) => {
        return pubsub.asyncIterableIterator(`ORDER_STATUS_${orderId}`);
      },
    },
  },
  Mutation: {
    updateOrderStatus: async (_, { id, status }, ctx) => {
      const order = await ctx.db.order.update({ where: { id }, data: { status } });
      await pubsub.publish(`ORDER_STATUS_${id}`, { orderStatusChanged: order });
      return { order, errors: [] };
    },
  },
};

Anti-Patterns

  • Exposing database schema directly as GraphQL schema
  • Resolving nested fields without DataLoader (causes N+1 queries)
  • Using offset-based pagination instead of cursor-based for large datasets
  • Throwing raw errors from resolvers instead of returning typed error payloads
  • Creating a single monolithic schema file instead of modular type definitions
  • Allowing unbounded queries without depth or complexity limits

Checklist

  • Relay-style cursor pagination for all list fields
  • DataLoader used for all batched entity lookups
  • Mutations return payload types with both result and error fields
  • Input types used for mutation arguments
  • Query depth and complexity limits configured
  • DataLoader instances created per-request in context
  • Schema split into domain-specific modules
  • Subscriptions use filtered topics to avoid broadcasting to all clients

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Web accessibility patterns for WCAG 2.2 compliance including ARIA, keyboard navigation, screen readers, and testing

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rohitg00/awesome-claude-code-toolkit2.7천2026년 5월 12일 업데이트

Route broad or ambiguous AgentKit SEO work to the right module while keeping context scoped. Use when a request spans multiple surfaces, asks for overall digital-presence strategy, involves provider or install architecture, needs agent-context planning, or the correct platform skill is unclear.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rohitg00/awesome-claude-code-toolkit2.7천2026년 5월 12일 업데이트

REST API design with resource naming, pagination, versioning, and OpenAPI spec generation

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rohitg00/awesome-claude-code-toolkit2.7천2026년 5월 12일 업데이트

Authentication and authorization patterns including OAuth2, JWT, RBAC, session management, and PKCE flows

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rohitg00/awesome-claude-code-toolkit2.7천2026년 5월 12일 업데이트

AWS cloud patterns for Lambda, ECS, S3, DynamoDB, and Infrastructure as Code with CDK/Terraform

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rohitg00/awesome-claude-code-toolkit2.7천2026년 5월 12일 업데이트

CI/CD pipeline patterns for GitHub Actions, GitLab CI, testing strategies, and deployment automation

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rohitg00/awesome-claude-code-toolkit2.7천2026년 5월 12일 업데이트

rohitg00의 스킬 모두 보기

이 스킬의 문제 신고