Skip to content

Orval-generated hooks are the only source of truth for API calls

The frontend had two ways to call the backend: ~45 hand-written hooks in src/api/hooks.ts calling authedFetch/authedJson directly, and Orval-generated hooks from the OpenAPI schema in src/api/generated.ts. We decided Orval is the only source of truth — hooks.ts no longer wraps raw fetch calls, it only composes Orval hooks (toggle wrappers, cache invalidation) where a component needs more than one generated hook can give it. Any component still calling authedFetch/authedJson directly is treated as unmigrated debt, not an accepted pattern — the fix is either to migrate it or to add the missing endpoint to the OpenAPI schema so Orval can generate it.

Considered Options

  • Keep both: hand-written hooks for custom logic (auth headers, retries, shaping), Orval for simple CRUD. Rejected — components had no way to tell which one to use for a new call, and the two diverged silently (e.g. bookmark and chat-reaction calls kept using raw fetch even after an equivalent Orval hook existed).
  • Generate everything with Orval, including toggle/composite behavior via custom Orval mutators. Rejected as more machinery than needed — a thin manual wrapper layer over generated hooks is simpler to read.

Consequences

  • Any backend endpoint the frontend needs must be documented in the OpenAPI schema (@extend_schema / serializer) before frontend work can start — schema completeness is now a blocking dependency for frontend features, not an afterthought.
  • frontend/openapi.json and src/api/generated.ts can drift out of sync with the live backend schema if bun run generate:api isn't re-run after a backend change; nothing currently fails CI if this happens.

Strum — Documentação.