Skill: ivangdavila/graphql (version 1.0.1)

--- name: GraphQL slug: graphql version: 1.0.1 description: Design GraphQL schemas and resolvers with proper performance, security, and error handling. metadata: {"clawdbot":{"emoji":"◈","os":["linux","darwin","win32"]}} --- ## Quick Reference | Topic | File | |-------|------| | Schema design patterns | `schema.md` | | Security and limits | `security.md` | | Performance optimization | `performance.md` | | Client-side patterns | `client.md` | ## N+1 Problem (Critical) - Each resolver runs independently—fetching user for each of 100 posts = 100 queries - DataLoader required: batches requests within single tick—100 posts = 1 user query - DataLoader per-request: create new instance per request—prevents cross-request caching - Even with DataLoader, watch for nested N+1—posts → comments → authors chains ## Schema Design - Fields nullable by default—make non-null explicit: `name: String!` - Input types separate from output—`CreateUserInput` vs `User`; allows different validation - Connections for pagination: `users(first: 10, after: "cursor")` returns `edges` + `pageInfo` - Avoid deeply nested types—flatten where possible; 5+ levels = resolver complexity ## Pagination - Cursor-based (Relay style): `first/after`, `last/before`—stable across insertions - Offset-based: `limit/offset`—simpler but skips or duplicates on concurrent writes - Return `pageInfo { hasNextPage, endCursor }`—client knows when to stop - `totalCount` expensive on large datasets—make optional or estimate ## Security Traps - Query depth limiting—prevent `{ user { friends { friends { friends... } } } }` - Query complexity scoring—count fields, multiply by list sizes; reject above threshold - Disable introspection in production—or protect with auth; schema is attack surface - Timeout per query—malicious queries can be slow without being deep - Rate limit by complexity, not just requests—one complex query = many simple ones ## Error Handling - Partial success normal—query returns data AND errors; check both - Errors array with path—shows which field failed: `"path": ["user", "posts", 0]` - Error extensions for codes—`"extensions": {"code": "FORBIDDEN"}`; don't parse message - Throw in resolver = null + error—parent nullable = partial data; parent non-null = error propagates up ## Resolver Patterns - Return object with ID, let sub-resolvers fetch details—avoids over-fetching at top level - `__resolveType` for unions/interfaces—required to determine concrete type - Context for auth, DataLoaders, DB connection—pass through resolver chain - Field-level auth in resolvers—check permissions per field, not just per query ## Mutations - Return modified object—client updates cache without re-fetch - Input validation before DB—return user-friendly error, not DB constraint violation - Idempotency for critical mutations—accept client-generated ID or idempotency key - One mutation per operation typically—batch mutations exist but complicate error handling ## Performance - Persisted queries: hash → query mapping—smaller payloads, prevents arbitrary queries - `@defer` for slow fields—returns fast fields first, streams slow ones (if supported) - Fragment colocation: components define data needs—reduces over-fetching - Query allowlisting: only registered queries in production—blocks exploratory attacks ## Subscriptions - WebSocket-based—`graphql-ws` protocol; separate from HTTP - Scaling: pub/sub needed—Redis or similar for multi-server broadcast - Filter at subscription level—don't push everything and filter client-side - Unsubscribe on disconnect—clean up resources; connection tracking required ## Client-Side - Normalized cache (Apollo, Relay)—deduplicate by ID; updates propagate automatically - Optimistic UI: predict mutation result—rollback if server differs - Error policies: decide per-query—ignore errors, return partial, or treat as failure - Fragment reuse—define once, use in multiple queries; keeps fields in sync ## Common Mistakes - No DataLoader—N+1 kills performance; one query becomes hundreds - Exposing internal errors—stack traces leak implementation details - No query limits—attackers craft expensive queries; DoS with single request - Over-fetching in resolvers—fetching full object when query only needs ID + name - Treating like REST—GraphQL is a graph; design for traversal, not resources


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)