spb/ultra-sharp-agent-skills Public
Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.
Python 100%
1---2name: designing-graphql-apis3description: Designs GraphQL APIs with schema-first typing, Relay-style cursor connections, DataLoader batching against N+1 queries, union result types for expected errors, and depth/complexity limits. Use when the user asks to design, review, or refactor a GraphQL schema or API, write type definitions or resolvers, add pagination to a GraphQL query, fix N+1 resolver performance, or structure mutations. Do not use for REST APIs (designing-rest-apis) or for real-time subscription transport infrastructure.4---56<!--7Author: Simon-Pierre Boucher8Contact: contact@spboucher.ai9-->1011# Designing GraphQL APIs1213## When to use / when NOT to use14- **Use for:** GraphQL schema design, type/resolver structure, connections, mutations, error modeling, query cost control.15- **Do NOT use for:** REST endpoint design → `designing-rest-apis`; WebSocket/subscription transport setup; client-side query writing.1617## Core rules18191. **Schema first, domain-shaped.** Design the SDL from the domain and client needs, then implement resolvers. Never mirror database tables 1:1 — expose what clients consume.20 - ✅ `type Order { total: Money!, placedAt: DateTime! }`21 - ❌ `type OrdersTbl { amt_cents: Int, created_ts: Int }`222. **Nullable by default; non-null (`!`) only when guaranteed.** A non-null field that fails nulls out its whole parent chain. Reserve `!` for IDs and fields the resolver can never fail to produce.233. **Paginate lists with Relay connections.** Any list that can grow gets `Connection`/`Edge`/`PageInfo` with `first`/`after` cursor args — never a bare unbounded `[Order!]!`.244. **DataLoader is the default N+1 fix.** Every resolver that fetches by ID goes through a per-request DataLoader that batches and caches. One query per collection of parents, not one per parent.255. **Mutations: verb-object names, one input, one payload.** `createOrder(input: CreateOrderInput!): CreateOrderPayload!`. Payload contains the changed object plus expected-error fields — never return the bare object.266. **Expected errors are union result types; the `errors` array is for exceptions only.**27 - ✅ `union CreateOrderResult = Order | ValidationError | OutOfStockError`28 - ❌ throwing for "email already taken" so clients parse `errors[0].message`297. **Cap query cost.** Enforce a depth limit (default 10) and a complexity budget (points per field × list multipliers); reject over-budget queries before execution.308. **Additive evolution only.** Add fields freely; never remove or change a type in place — `@deprecated(reason: "...")` first, remove in a coordinated major cycle.3132## Workflow33341. List client use cases; sketch queries clients should be able to write.352. Write the SDL: types, connections (rule 3), mutations (rule 5), error unions (rule 6).363. Apply nullability discipline (rule 2) type by type.374. Plan resolvers: mark every by-ID fetch as a DataLoader (rule 4).385. Set depth/complexity limits (rule 7).396. **Validate:** run the schema through a linter (`npx graphql-schema-linter schema.graphql` — install with `npm i -g graphql-schema-linter` if missing), execute the sketched queries from step 1 against a stub server, and log SQL for one nested query to confirm no N+1 (query count must be O(depth), not O(rows)). Fix and repeat.4041## Edge cases & failure modes42- **A field that is expensive for some parents** → split it (`Order.summary` cheap, `Order.analytics` separate type) so cheap queries don't pay.43- **Polymorphic lists** → interfaces when types share fields, unions when they don't; always include `__typename` handling in examples.44- **File uploads** → do not tunnel through GraphQL; issue a presigned URL via mutation and upload out-of-band.45- **Global object identity** → give every fetchable type a globally unique `id: ID!` (base64 `Type:dbId`) so caches and `node(id:)` refetching work.4647## References48Deeper patterns (connection SDL, DataLoader implementation, complexity scoring): see [references/patterns.md](references/patterns.md).49