Files
atelier/domains/api/graphql.md
T
2026-08-05 00:30:31 +00:00

53 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GraphQL — Derived Rules
> Derives from `domains/api/first-principles.md`. Applies P1P10 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.