skills/graphql-patterns/SKILL.md
Schema design, resolver patterns, DataLoader, N+1 prevention, and subscription patterns for GraphQL APIs.
npx skillsauth add rubicanjr/FinCognis graphql-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Production-grade GraphQL API design with performance and type safety.
# 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
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
}
}
}
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 }
}
}
}
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
}
}
}
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,
})
]
})
development
Goal-based workflow orchestration - routes tasks to specialist agents based on user goals
tools
Wiring Verification
development
Connection management, room patterns, reconnection strategies, message buffering, and binary protocol design.
development
Screenshot comparison QA for frontend development. Takes a screenshot of the current implementation, scores it across multiple visual dimensions, and returns a structured PASS/REVISE/FAIL verdict with concrete fixes. Use when implementing UI from a design reference or verifying visual correctness.