# GraphQL — Derived Rules > Derives from `domains/api/first-principles.md`. Applies P1–P10 to GraphQL specifically. ## Schema First (P1 Contract Fidelity) - The schema is the contract. Every field has a type, a description, and a deprecation status. - The schema is versioned. Breaking schema changes (removing a field, changing a type) require a deprecation cycle. - Never expose raw database types in the schema. Map them to domain types. ## Query Design (P2 Clarity, P3 Predictability) - Field names are nouns, camelCase: `userOrders`, not `UserOrders` or `user_orders`. - Arguments are descriptive: `first`, `after`, `orderBy` — not `arg1`, `arg2`. - Connections for lists: `users(first: 10, after: "cursor")` — never bare arrays. - Mutations are verbs: `createUser`, `deleteOrder` — not `userCreate`. ## N+1 Prevention (P7 Performance) - Use a dataloader for every list field that resolves to another resource. - A resolver that does a database query per item is an N+1 bug. - Test resolvers under a list query, not just a single-item query. ## Authorization at the Field Level (P8 Security) - Every resolver checks authorization. The query graph is not a trust boundary by default. - A user who can query `user { email }` is not automatically authorized to query `user { passwordHash }`. - Field-level authz is the floor, not an optimization. ## Deprecation (P5 Versioning, P10 Stability) - Deprecate fields with `@deprecated(reason: "...")`. Never remove a field without deprecating first. - A deprecated field is removed in the next major schema version, not sooner. - Track field usage. A deprecated field with no usage can be removed sooner. ## Error Handling (P9 Error Transparency) - Errors are partial by default: a query can return data and errors simultaneously. - Errors are structured: `{ message, path, extensions: { code, ... } }`. - Use `extensions.code` for machine-consumable error types, not free-text messages. - Never swallow a resolver error silently. A null field with no error is a bug. ## Complexity Budget (P7 Performance, P8 Security) - Enforce a query complexity limit. Unbounded depth/breadth is a DoS vector. - Cost-based analysis (not just depth) catches expensive nested queries. - Reject queries over budget with a 400, not a 500. ## Federation (P6 Composability) - A federated subgraph owns its entities. Cross-graph references use `@external` and `@requires`. - Never reach into another subgraph's database. The graph boundary is the contract. - The gateway composes; subgraphs do not know about each other.