Skip to content

Add Orval + Zod runtime API response validation - #1414

Closed
hidden4003 wants to merge 2 commits into
masterfrom
feat/orval-response-validation
Closed

Add Orval + Zod runtime API response validation#1414
hidden4003 wants to merge 2 commits into
masterfrom
feat/orval-response-validation

Conversation

@hidden4003

Copy link
Copy Markdown
Member

Summary

  • Adds Orval to generate TypeScript types + Zod schemas from Shoko Server's live OpenAPI v3 spec into src/core/api/generated/ (committed; regenerate locally via pnpm orval against a running server, since CI has none).
  • Adds validateResponse() / SchemaValidationError (src/core/api/validateResponse.ts) so a response that doesn't match its schema throws instead of letting malformed data silently reach components as undefined fields.
  • queryClient.ts surfaces a SchemaValidationError as an immediate "Invalid API Response" toast, skipping the existing 4x retry (retrying won't fix a shape mismatch).
  • Pilot conversion of tag and the tag-related series hooks to prove the pattern; the rest of src/core/react-query/ is intentionally left for incremental follow-up.
  • Along the way this caught two real, pre-existing bugs: TagType.Source was typed as a closed 'AniDB' | 'User' union when the server doesn't actually constrain it, and a cleanString() helper only guarded against undefined, not null (which the real schema allows for Description).

Draft because this establishes a new pattern (Orval + Zod validation, a src/core/api/generated directory excluded from oxlint, a few workarounds for upstream Orval/spec quirks) that I'd like other maintainers to weigh in on before it's adopted more broadly.

Test plan

  • pnpm dprint, pnpm oxlint, pnpm stylelint, pnpm tscheck all pass
  • pnpm orval runs cleanly against a live Shoko Server dev instance
  • pnpm start boots and proxies correctly
  • Manual click-through of Collection list view and a series detail page's tags panel (please verify — I didn't have a browser automation tool available in my sandbox)

🤖 Generated with Claude Code

Hand-written API response types can silently drift from what Shoko
Server actually returns, letting malformed-but-valid-JSON responses
reach components as undefined fields in unrelated places. Orval now
generates types and Zod schemas from the server's live OpenAPI spec
into src/core/api/generated/ (committed, regenerate via `pnpm orval`),
and a validateResponse() helper throws a SchemaValidationError on
mismatch, which queryClient.ts surfaces as an immediate toast instead
of retrying 4x. tag and series-tags react-query hooks are converted as
a pilot; the rest of src/core/react-query/ can follow the same pattern
incrementally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hidden4003
hidden4003 requested a review from harshithmohan July 26, 2026 14:45
@harshithmohan

Copy link
Copy Markdown
Member

So right now we only generate types and not the hooks also?

Chaining .then(data => validateResponse(schema, data, context)) at every
call site was easy to forget and added boilerplate. Schemas now travel as
a `schema` field on the request config instead; the axios/axiosV2/axiosPlex
response interceptor validates automatically when present, and the
resolved type is inferred from the schema with no explicit generic needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hidden4003

Copy link
Copy Markdown
Member Author

So right now we only generate types and not the hooks also?

Our hooks have custom logic in them which is why I did not set up hook generation as well, I can add it if you want to see how it looks.

@hidden4003 hidden4003 closed this Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants