[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6NTY1DKPP7A3NBGS3MFD5Q1":3,"skill-same:01M6NTY1DKPP7A3NBGS3MFD5Q1":60},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":33,"linkOnly":34,"markdownContent":35,"markdownBody":36,"frontMatter":37,"refName":38,"commitSha":39,"skillDirectory":40,"forksCount":14,"repositoryHtmlUrl":41,"repositoryHomepageUrl":6,"ownerHtmlUrl":42,"ownerBlogUrl":43,"ownerDescription":44,"updatedAt":45,"install":46,"resources":59},"01M6NTY1DKPP7A3NBGS3MFD5Q1","graphql-patterns",null,"Schema design, resolver patterns, DataLoader, N+1 prevention, and subscription patterns for GraphQL APIs.","rubicanjr\u002FFinCognis","rubicanjr","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F234912519?v=4","01M6NT156NND8J474G34SB3923","Rubi","01M6NT15B70RWXE1N3YV0BZY8A",0,"MIT","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fgraphql-patterns\u002Fskill.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32],"GraphQL","patterns","graphql patterns","design","prevention","schema","schema design","resolver","for","2026-11-10 10:12:30",false,"---\nname: graphql-patterns\ndescription: Schema design, resolver patterns, DataLoader, N+1 prevention, and subscription patterns for GraphQL APIs.\n---\n\n# GraphQL Patterns\n\nProduction-grade GraphQL API design with performance and type safety.\n\n## Schema Design Principles\n\n```graphql\n# Use interfaces for shared fields\ninterface Node {\n  id: ID!\n  createdAt: DateTime!\n  updatedAt: DateTime!\n}\n\ntype User implements Node {\n  id: ID!\n  createdAt: DateTime!\n  updatedAt: DateTime!\n  email: String!\n  displayName: String!\n  posts(first: Int, after: String): PostConnection!\n}\n\n# Relay-style pagination (cursor-based)\ntype PostConnection {\n  edges: [PostEdge!]!\n  pageInfo: PageInfo!\n  totalCount: Int!\n}\n\ntype PostEdge {\n  node: Post!\n  cursor: String!\n}\n\ntype PageInfo {\n  hasNextPage: Boolean!\n  hasPreviousPage: Boolean!\n  startCursor: String\n  endCursor: String\n}\n\n# Input types for mutations\ninput CreatePostInput {\n  title: String!\n  body: String!\n  tags: [String!]\n}\n\n# Union for mutation results (error handling without exceptions)\ntype CreatePostSuccess {\n  post: Post!\n}\n\ntype ValidationError {\n  field: String!\n  message: String!\n}\n\nunion CreatePostResult = CreatePostSuccess | ValidationError\n```\n\n## DataLoader - N+1 Prevention\n\n```typescript\nimport DataLoader from 'dataloader'\n\n\u002F\u002F Batch function: receives array of keys, returns array of results in same order\nfunction createUserLoader(db: Database) {\n  return new DataLoader\u003Cstring, User | null>(async (userIds) => {\n    const users = await db.user.findMany({\n      where: { id: { in: [...userIds] } }\n    })\n    const userMap = new Map(users.map(u => [u.id, u]))\n    \u002F\u002F MUST return in same order as input keys\n    return userIds.map(id => userMap.get(id) ?? null)\n  })\n}\n\n\u002F\u002F Create per-request context (loaders are NOT shared across requests)\nfunction createContext(req: Request) {\n  const db = getDatabase()\n  return {\n    db,\n    loaders: {\n      user: createUserLoader(db),\n      post: createPostLoader(db),\n      comment: createCommentLoader(db),\n    }\n  }\n}\n\n\u002F\u002F Resolver uses loader instead of direct DB query\nconst resolvers = {\n  Post: {\n    author: (post: Post, _args: unknown, ctx: Context) => {\n      return ctx.loaders.user.load(post.authorId)  \u002F\u002F batched automatically\n    }\n  }\n}\n```\n\n## Resolver Pattern with Validation\n\n```typescript\nimport { z } from 'zod'\n\nconst CreatePostSchema = z.object({\n  title: z.string().min(1).max(200),\n  body: z.string().min(10).max(50000),\n  tags: z.array(z.string()).max(10).optional()\n})\n\nconst resolvers = {\n  Mutation: {\n    createPost: async (_parent: unknown, args: { input: unknown }, ctx: Context) => {\n      \u002F\u002F Auth guard\n      if (!ctx.currentUser) {\n        throw new AuthenticationError('Login required')\n      }\n\n      \u002F\u002F Input validation\n      const parsed = CreatePostSchema.safeParse(args.input)\n      if (!parsed.success) {\n        return {\n          __typename: 'ValidationError',\n          field: parsed.error.issues[0].path.join('.'),\n          message: parsed.error.issues[0].message\n        }\n      }\n\n      const post = await ctx.db.post.create({\n        data: { ...parsed.data, authorId: ctx.currentUser.id }\n      })\n\n      return { __typename: 'CreatePostSuccess', post }\n    }\n  }\n}\n```\n\n## Subscription Patterns\n\n```typescript\nimport { PubSub, withFilter } from 'graphql-subscriptions'\n\nconst pubsub = new PubSub()  \u002F\u002F Use RedisPubSub in production\n\nconst EVENTS = {\n  POST_CREATED: 'POST_CREATED',\n  COMMENT_ADDED: 'COMMENT_ADDED',\n} as const\n\nconst resolvers = {\n  Subscription: {\n    commentAdded: {\n      \u002F\u002F Filter: only deliver to subscribers watching this post\n      subscribe: withFilter(\n        () => pubsub.asyncIterableIterator(EVENTS.COMMENT_ADDED),\n        (payload, variables) => payload.commentAdded.postId === variables.postId\n      )\n    }\n  },\n  Mutation: {\n    addComment: async (_p: unknown, args: { postId: string; body: string }, ctx: Context) => {\n      const comment = await ctx.db.comment.create({\n        data: { postId: args.postId, body: args.body, authorId: ctx.currentUser!.id }\n      })\n      await pubsub.publish(EVENTS.COMMENT_ADDED, { commentAdded: comment })\n      return comment\n    }\n  }\n}\n```\n\n## Query Depth & Complexity Limiting\n\n```typescript\nimport depthLimit from 'graphql-depth-limit'\nimport { createComplexityLimitRule } from 'graphql-validation-complexity'\n\nconst server = new ApolloServer({\n  schema,\n  validationRules: [\n    depthLimit(7),                          \u002F\u002F Max 7 levels deep\n    createComplexityLimitRule(1000, {        \u002F\u002F Max 1000 complexity points\n      scalarCost: 1,\n      objectCost: 2,\n      listFactor: 10,\n    })\n  ]\n})\n```\n\n## Checklist\n\n- [ ] Cursor-based pagination (not offset-based) for all list fields\n- [ ] DataLoader for every relationship resolver (one loader per entity per request)\n- [ ] Input validation with zod\u002Fjoi before DB operations\n- [ ] Query depth limit (7-10) and complexity limit\n- [ ] Auth checks in resolvers, not middleware (field-level control)\n- [ ] Union types for mutation results instead of throwing errors\n- [ ] Persisted queries in production (disable arbitrary queries)\n- [ ] Schema versioned via interfaces, not breaking changes\n\n## Anti-Patterns\n\n- Exposing database IDs directly (use opaque\u002Fglobal IDs)\n- Resolver doing N+1 queries without DataLoader\n- Sharing DataLoader instances across requests (stale data, auth leak)\n- Offset pagination on large datasets (performance cliff)\n- God queries: single resolver fetching entire object graph\n- Putting business logic in resolvers (keep resolvers thin, use service layer)\n","# GraphQL Patterns\n\nProduction-grade GraphQL API design with performance and type safety.\n\n## Schema Design Principles\n\n```graphql\n# Use interfaces for shared fields\ninterface Node {\n  id: ID!\n  createdAt: DateTime!\n  updatedAt: DateTime!\n}\n\ntype User implements Node {\n  id: ID!\n  createdAt: DateTime!\n  updatedAt: DateTime!\n  email: String!\n  displayName: String!\n  posts(first: Int, after: String): PostConnection!\n}\n\n# Relay-style pagination (cursor-based)\ntype PostConnection {\n  edges: [PostEdge!]!\n  pageInfo: PageInfo!\n  totalCount: Int!\n}\n\ntype PostEdge {\n  node: Post!\n  cursor: String!\n}\n\ntype PageInfo {\n  hasNextPage: Boolean!\n  hasPreviousPage: Boolean!\n  startCursor: String\n  endCursor: String\n}\n\n# Input types for mutations\ninput CreatePostInput {\n  title: String!\n  body: String!\n  tags: [String!]\n}\n\n# Union for mutation results (error handling without exceptions)\ntype CreatePostSuccess {\n  post: Post!\n}\n\ntype ValidationError {\n  field: String!\n  message: String!\n}\n\nunion CreatePostResult = CreatePostSuccess | ValidationError\n```\n\n## DataLoader - N+1 Prevention\n\n```typescript\nimport DataLoader from 'dataloader'\n\n\u002F\u002F Batch function: receives array of keys, returns array of results in same order\nfunction createUserLoader(db: Database) {\n  return new DataLoader\u003Cstring, User | null>(async (userIds) => {\n    const users = await db.user.findMany({\n      where: { id: { in: [...userIds] } }\n    })\n    const userMap = new Map(users.map(u => [u.id, u]))\n    \u002F\u002F MUST return in same order as input keys\n    return userIds.map(id => userMap.get(id) ?? null)\n  })\n}\n\n\u002F\u002F Create per-request context (loaders are NOT shared across requests)\nfunction createContext(req: Request) {\n  const db = getDatabase()\n  return {\n    db,\n    loaders: {\n      user: createUserLoader(db),\n      post: createPostLoader(db),\n      comment: createCommentLoader(db),\n    }\n  }\n}\n\n\u002F\u002F Resolver uses loader instead of direct DB query\nconst resolvers = {\n  Post: {\n    author: (post: Post, _args: unknown, ctx: Context) => {\n      return ctx.loaders.user.load(post.authorId)  \u002F\u002F batched automatically\n    }\n  }\n}\n```\n\n## Resolver Pattern with Validation\n\n```typescript\nimport { z } from 'zod'\n\nconst CreatePostSchema = z.object({\n  title: z.string().min(1).max(200),\n  body: z.string().min(10).max(50000),\n  tags: z.array(z.string()).max(10).optional()\n})\n\nconst resolvers = {\n  Mutation: {\n    createPost: async (_parent: unknown, args: { input: unknown }, ctx: Context) => {\n      \u002F\u002F Auth guard\n      if (!ctx.currentUser) {\n        throw new AuthenticationError('Login required')\n      }\n\n      \u002F\u002F Input validation\n      const parsed = CreatePostSchema.safeParse(args.input)\n      if (!parsed.success) {\n        return {\n          __typename: 'ValidationError',\n          field: parsed.error.issues[0].path.join('.'),\n          message: parsed.error.issues[0].message\n        }\n      }\n\n      const post = await ctx.db.post.create({\n        data: { ...parsed.data, authorId: ctx.currentUser.id }\n      })\n\n      return { __typename: 'CreatePostSuccess', post }\n    }\n  }\n}\n```\n\n## Subscription Patterns\n\n```typescript\nimport { PubSub, withFilter } from 'graphql-subscriptions'\n\nconst pubsub = new PubSub()  \u002F\u002F Use RedisPubSub in production\n\nconst EVENTS = {\n  POST_CREATED: 'POST_CREATED',\n  COMMENT_ADDED: 'COMMENT_ADDED',\n} as const\n\nconst resolvers = {\n  Subscription: {\n    commentAdded: {\n      \u002F\u002F Filter: only deliver to subscribers watching this post\n      subscribe: withFilter(\n        () => pubsub.asyncIterableIterator(EVENTS.COMMENT_ADDED),\n        (payload, variables) => payload.commentAdded.postId === variables.postId\n      )\n    }\n  },\n  Mutation: {\n    addComment: async (_p: unknown, args: { postId: string; body: string }, ctx: Context) => {\n      const comment = await ctx.db.comment.create({\n        data: { postId: args.postId, body: args.body, authorId: ctx.currentUser!.id }\n      })\n      await pubsub.publish(EVENTS.COMMENT_ADDED, { commentAdded: comment })\n      return comment\n    }\n  }\n}\n```\n\n## Query Depth & Complexity Limiting\n\n```typescript\nimport depthLimit from 'graphql-depth-limit'\nimport { createComplexityLimitRule } from 'graphql-validation-complexity'\n\nconst server = new ApolloServer({\n  schema,\n  validationRules: [\n    depthLimit(7),                          \u002F\u002F Max 7 levels deep\n    createComplexityLimitRule(1000, {        \u002F\u002F Max 1000 complexity points\n      scalarCost: 1,\n      objectCost: 2,\n      listFactor: 10,\n    })\n  ]\n})\n```\n\n## Checklist\n\n- [ ] Cursor-based pagination (not offset-based) for all list fields\n- [ ] DataLoader for every relationship resolver (one loader per entity per request)\n- [ ] Input validation with zod\u002Fjoi before DB operations\n- [ ] Query depth limit (7-10) and complexity limit\n- [ ] Auth checks in resolvers, not middleware (field-level control)\n- [ ] Union types for mutation results instead of throwing errors\n- [ ] Persisted queries in production (disable arbitrary queries)\n- [ ] Schema versioned via interfaces, not breaking changes\n\n## Anti-Patterns\n\n- Exposing database IDs directly (use opaque\u002Fglobal IDs)\n- Resolver doing N+1 queries without DataLoader\n- Sharing DataLoader instances across requests (stale data, auth leak)\n- Offset pagination on large datasets (performance cliff)\n- God queries: single resolver fetching entire object graph\n- Putting business logic in resolvers (keep resolvers thin, use service layer)\n",{"name":5,"description":7},"main","70e1c350500ee750987420bbae48837afa906b7e","skills\u002Fgraphql-patterns","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis","https:\u002F\u002Fgithub.com\u002Frubicanjr","linktr.ee\u002Ffinanskodu","Industrial Engineer & CIO @FinansKodu. Architecting AI-driven financial agents and autonomous workflows. 🏗️ Building systems, not just prompts.","2026-06-09 13:50:15",{"source":8,"skillName":5,"commands":47,"prompt":57,"zipAvailable":58},[48,51,54],{"tool":49,"command":50},"npx","npx skills add rubicanjr\u002FFinCognis --skill graphql-patterns",{"tool":52,"command":53},"bunx","bunx skills add rubicanjr\u002FFinCognis --skill graphql-patterns",{"tool":55,"command":56},"pnpm","pnpm dlx skills add rubicanjr\u002FFinCognis --skill graphql-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fgraphql-patterns\u002Fskill.md",true,[],[61,80,89,105,134,153],{"skillNo":62,"name":63,"nameZh":6,"description":64,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":65,"categories":66,"keywords":70,"publishedAt":33},"01M6NWS9DAXJTADT4T5MB49X8W","workflow-router","Goal-based workflow orchestration - routes tasks to specialist agents based on user goals","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow-router\u002FSKILL.md",[67],{"categoryNo":68,"categoryName":69,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[71,72,73,74,75,76,77,78,79],"workflow router","workflow","router","orchestration","goals","goal","user","workflow orchestration","tasks",{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":33},"01M6NWS3RETSC0SHP16HP66E14","wiring","Wiring Verification","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fwiring\u002FSKILL.md",[],[82,87,88],"verification","false",{"skillNo":90,"name":91,"nameZh":6,"description":92,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":96,"publishedAt":33},"01M6NWS0XW9CVQZBDA1EGW0TWC","websocket-patterns","Connection management, room patterns, reconnection strategies, message buffering, and binary protocol design.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fwebsocket-patterns\u002Fskill.md",[95],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[97,25,98,27,99,100,101,102,103,104],"websocket","websocket patterns","protocol","management","message","strategies","binary","connection",{"skillNo":106,"name":107,"nameZh":6,"description":108,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":115,"publishedAt":33},"01M6NWRDQDX9ZNETAWRRMQWV52","visual-verdict","Screenshot comparison QA for frontend development. Takes a screenshot of the current implementation, scores it across multiple visual dimensions, and returns a structured PASS\u002FREVISE\u002FFAIL verdict with concrete fixes. Use when implementing UI from a design reference or verifying visual correctness.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fvisual-verdict\u002FSKILL.md",[111,114],{"categoryNo":112,"categoryName":113,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":68,"categoryName":69,"taxonomyType":21,"taxonomyTypeDesc":22},[116,117,118,119,120,121,122,123,124,32,125,126,127,27,128,129,130,131,132,133],"visual","verdict","visual verdict","ui","across","revise","comparison","correctness","implementing","fixes","dimensions","frontend","development","qa","screenshot","returns","use","frontend development",{"skillNo":135,"name":136,"nameZh":6,"description":137,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":138,"categories":139,"keywords":140,"publishedAt":33},"01M6NWR8HSNRS7MNDK2J02QYMN","verification-loop","Comprehensive verification system covering build, types, lint, tests, security, and diff review before a PR.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fverification-loop\u002FSKILL.md",[],[141,142,87,143,144,145,146,147,148,149,150,151,152],"verification loop","loop","lint","review","security","pr","system","diff","build","tests","diff review","types",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":157,"categories":158,"keywords":159,"publishedAt":33},"01M6NWR4HHSFR0VR5EXFRWA747","vector-db-patterns","Embedding strategies, ANN algorithms, hybrid search, RAG chunking strategies, and reranking for semantic search and retrieval.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fvector-db-patterns\u002Fskill.md",[],[160,161,162,163,25,164,165,166,167,102,168,169,170,171,172,32,173,174],"vector db patterns","vector db","db","vector","semantic search","search","embedding strategies","algorithms","chunking","semantic","retrieval","embedding","Hybrid Search","hybrid","rag"]