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

graphql-patterns

Schema design, resolver patterns, DataLoader, N+1 prevention, and subscription patterns for GraphQL APIs.

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

설치 방법 보기

포함된 파일(1)

  • SKILL.md5.4 KB

SKILL.md(원문)

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

GraphQL Patterns

Production-grade GraphQL API design with performance and type safety.

Schema Design Principles

# Use interfaces for shared fields
interface Node {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime!
}

type User implements Node {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime!
  email: String!
  displayName: String!
  posts(first: Int, after: String): PostConnection!
}

# Relay-style pagination (cursor-based)
type PostConnection {
  edges: [PostEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type PostEdge {
  node: Post!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

# Input types for mutations
input CreatePostInput {
  title: String!
  body: String!
  tags: [String!]
}

# Union for mutation results (error handling without exceptions)
type CreatePostSuccess {
  post: Post!
}

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

union CreatePostResult = CreatePostSuccess | ValidationError

DataLoader - N+1 Prevention

import DataLoader from 'dataloader'

// Batch function: receives array of keys, returns array of results in same order
function createUserLoader(db: Database) {
  return new DataLoader<string, User | null>(async (userIds) => {
    const users = await db.user.findMany({
      where: { id: { in: [...userIds] } }
    })
    const userMap = new Map(users.map(u => [u.id, u]))
    // MUST return in same order as input keys
    return userIds.map(id => userMap.get(id) ?? null)
  })
}

// Create per-request context (loaders are NOT shared across requests)
function createContext(req: Request) {
  const db = getDatabase()
  return {
    db,
    loaders: {
      user: createUserLoader(db),
      post: createPostLoader(db),
      comment: createCommentLoader(db),
    }
  }
}

// Resolver uses loader instead of direct DB query
const resolvers = {
  Post: {
    author: (post: Post, _args: unknown, ctx: Context) => {
      return ctx.loaders.user.load(post.authorId)  // batched automatically
    }
  }
}

Resolver Pattern with Validation

import { z } from 'zod'

const CreatePostSchema = z.object({
  title: z.string().min(1).max(200),
  body: z.string().min(10).max(50000),
  tags: z.array(z.string()).max(10).optional()
})

const resolvers = {
  Mutation: {
    createPost: async (_parent: unknown, args: { input: unknown }, ctx: Context) => {
      // Auth guard
      if (!ctx.currentUser) {
        throw new AuthenticationError('Login required')
      }

      // Input validation
      const parsed = CreatePostSchema.safeParse(args.input)
      if (!parsed.success) {
        return {
          __typename: 'ValidationError',
          field: parsed.error.issues[0].path.join('.'),
          message: parsed.error.issues[0].message
        }
      }

      const post = await ctx.db.post.create({
        data: { ...parsed.data, authorId: ctx.currentUser.id }
      })

      return { __typename: 'CreatePostSuccess', post }
    }
  }
}

Subscription Patterns

import { PubSub, withFilter } from 'graphql-subscriptions'

const pubsub = new PubSub()  // Use RedisPubSub in production

const EVENTS = {
  POST_CREATED: 'POST_CREATED',
  COMMENT_ADDED: 'COMMENT_ADDED',
} as const

const resolvers = {
  Subscription: {
    commentAdded: {
      // Filter: only deliver to subscribers watching this post
      subscribe: withFilter(
        () => pubsub.asyncIterableIterator(EVENTS.COMMENT_ADDED),
        (payload, variables) => payload.commentAdded.postId === variables.postId
      )
    }
  },
  Mutation: {
    addComment: async (_p: unknown, args: { postId: string; body: string }, ctx: Context) => {
      const comment = await ctx.db.comment.create({
        data: { postId: args.postId, body: args.body, authorId: ctx.currentUser!.id }
      })
      await pubsub.publish(EVENTS.COMMENT_ADDED, { commentAdded: comment })
      return comment
    }
  }
}

Query Depth & Complexity Limiting

import depthLimit from 'graphql-depth-limit'
import { createComplexityLimitRule } from 'graphql-validation-complexity'

const server = new ApolloServer({
  schema,
  validationRules: [
    depthLimit(7),                          // Max 7 levels deep
    createComplexityLimitRule(1000, {        // Max 1000 complexity points
      scalarCost: 1,
      objectCost: 2,
      listFactor: 10,
    })
  ]
})

Checklist

  • Cursor-based pagination (not offset-based) for all list fields
  • DataLoader for every relationship resolver (one loader per entity per request)
  • Input validation with zod/joi before DB operations
  • Query depth limit (7-10) and complexity limit
  • Auth checks in resolvers, not middleware (field-level control)
  • Union types for mutation results instead of throwing errors
  • Persisted queries in production (disable arbitrary queries)
  • Schema versioned via interfaces, not breaking changes

Anti-Patterns

  • Exposing database IDs directly (use opaque/global IDs)
  • Resolver doing N+1 queries without DataLoader
  • Sharing DataLoader instances across requests (stale data, auth leak)
  • Offset pagination on large datasets (performance cliff)
  • God queries: single resolver fetching entire object graph
  • Putting business logic in resolvers (keep resolvers thin, use service layer)

리뷰

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

댓글

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

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

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

0 / 1,000자

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

같은 저장소의 스킬

WCAG 2.2 AA compliance, ARIA patterns, keyboard navigation, screen reader optimization

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

vibeeval/vibecosystem5382026년 8월 9일 업데이트

axe-core integration, WCAG 2.2 AA checklist, keyboard navigation testing, screen reader testing, and ARIA pattern validation.

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

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Steam-style achievement system with XP, levels, streaks, and skill trees. Gamifies the development workflow. 25 achievements across 5 categories.

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

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Framework for measuring and tracking agent response quality over time. Detects regressions before they reach production. Use when evaluating agent changes, auditing quality, or establishing performance baselines.

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

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Agent Context Isolation

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

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Agent ve skill dosyalarinin yapisal dogrulamasi. Frontmatter kontrol, naming convention, zorunlu bolum kontrolu, tutarlilik denetimi. Yeni agent/skill eklendiginde veya mevcut dosyalar duzenlediginde otomatik calistirilir.

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

vibeeval/vibecosystem5382026년 8월 9일 업데이트

vibeeval의 스킬 모두 보기

이 스킬의 문제 신고