Skip to main content

graphql-expert

GraphQL API design. Schema design, resolvers, N+1 problem, subscriptions, federation, Apollo, Pothos.

Informações da origem

Repositório
skeletorflet/opencode-kit
Última atividade na origem
3 de abril de 2026 às 15:32
Idioma detectado do SKILL.md
inglês
Estrelas
10
Forks
1

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
graphql-expert
description
GraphQL API design. Schema design, resolvers, N+1 problem, subscriptions, federation, Apollo, Pothos.
# GraphQL Expert > GraphQL: ask for exactly what you need, nothing more. --- ## 1. Schema Design Principles ```graphql # Think in graphs, not resources # Good: model the domain type User { id: ID! name: String! email: String! posts(first: Int, after: String): PostConnection! role: UserRole! } # Connections for pagination (Relay spec) 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! content: String! published: Boolean! = false } ``` --- ## 2. Resolver Pattern ```ts // GraphQL Yoga + Pothos (type-safe schema builder) import SchemaBuilder from "@pothos/core"; import PrismaPlugin from "@pothos/plugin-prisma"; const builder = new SchemaBuilder<{ PrismaTypes: PrismaTypes; Context: Context; }>({ plugins: [PrismaPlugin], prisma: { client: db }, }); builder.queryField("user", (t) => t.prismaField({ type: "User", args: { id: t.arg.id({ required: true }) }, resolve: async (query, _, { id }, { user }) => { if (!user) throw new GraphQLError("Unauthorized", { extensions: { code: "UNAUTHORIZED" } }); return db.user.findUniqueOrThrow({ ...query, where: { id } }); }, }) ); ``` --- ## 3. N+1 Problem & DataLoader ```ts // ❌ N+1: resolves author for each post individually posts.map(post => db.user.findUnique({ where: { id: post.authorId } })); // = 1 query for posts + N queries for authors // ✅ DataLoader: batch + dedupe import DataLoader from "dataloader"; const userLoader = new DataLoader<string, User>(async (ids) => { const users = await db.user.findMany({ where: { id: { in: ids as string[] } } }); return ids.map(id => users.find(u => u.id === id)!); }); // In resolver: now batches into single query const author = await userLoader.load(post.authorId); // Create per-request (in context) function createContext() { return { userLoader: new DataLoader(batchUsers), postLoader: new DataLoader(batchPosts), }; } ``` --- ## 4. Mutations Pattern ```graphql type Mutation { createPost(input: CreatePostInput!): CreatePostPayload! updatePost(id: ID!, input: UpdatePostInput!): UpdatePostPayload! deletePost(id: ID!): DeletePostPayload! } # Always return payload type (not raw object) # Allows adding fields later without breaking change type CreatePostPayload { post: Post errors: [UserError!]! } type UserError { field: [String!] message: String! } ``` --- ## 5. Subscriptions ```ts // GraphQL Yoga subscription builder.subscriptionField("messageAdded", (t) => t.field({ type: "Message", args: { channelId: t.arg.id({ required: true }) }, subscribe: async function* (_, { channelId }) { const sub = pubSub.subscribe(`channel:${channelId}`); for await (const message of sub) { yield message; } }, resolve: (message) => message, }) ); ``` --- ## 6. Error Handling ```ts import { GraphQLError } from "graphql"; // Typed error codes for clients throw new GraphQLError("Post not found", { extensions: { code: "NOT_FOUND", http: { status: 404 }, }, }); // Error codes enum ErrorCode { UNAUTHORIZED = "UNAUTHORIZED", FORBIDDEN = "FORBIDDEN", NOT_FOUND = "NOT_FOUND", VALIDATION_ERROR = "VALIDATION_ERROR", INTERNAL_ERROR = "INTERNAL_ERROR", } ``` --- ## 7. Security ```ts // Query depth limit import depthLimit from "graphql-depth-limit"; const server = createYoga({ schema, validationRules: [depthLimit(10)], }); // Query complexity import { createComplexityLimitRule } from "graphql-validation-complexity"; validationRules: [createComplexityLimitRule(1000)] // Disable introspection in production introspection: process.env.NODE_ENV === "development" // Persisted queries (only allow known queries) // → Major security improvement for production ``` --- ## 8. Federation (Apollo) ```graphql # users-service type User @key(fields: "id") { id: ID! name: String! } # posts-service extends User type extend type User @key(fields: "id") { id: ID! @external posts: [Post!]! } ``` --- ## 9. Anti-Patterns | ❌ Don\'t | ✅ Do | |----------|-------| | REST-like schema (getUserById) | Graph-centric (user(id:)) | | Return raw objects from mutations | Return payload types | | Resolve N+1 without DataLoader | Always use DataLoader | | Enable introspection in prod | Disable + use persisted queries | | No depth/complexity limits | Protect against DoS |
Ver no GitHub