feat!: validate API responses with zod schemas generated from the OpenAPI spec - #1016
Open
vdusek wants to merge 5 commits into
Open
feat!: validate API responses with zod schemas generated from the OpenAPI spec#1016vdusek wants to merge 5 commits into
vdusek wants to merge 5 commits into
Conversation
The spec now describes `Task.isPublic`, `Task.publicConfig` and `TaskPublicConfig`, so the hand-written gap filling them is dropped and `TaskPublicConfig` is declared on top of the generated schema. That removes `categorization`, which the API does not return, and makes `publishedAt` optional and read-only, as the spec states.
…nAPI spec Every response a resource method turns into a typed value is now checked against a zod schema generated from the specification (`scripts/generate_schemas.mts` -> `src/generated/schemas.ts`), widened in `src/schemas.ts` where the API is known to deviate from it. Unknown fields and enum values pass through; anything else that does not match throws `ResponseValidationError`. The pipeline is `pnpm generate:models` and the nightly workflow regenerates both artifacts. BREAKING CHANGE: a response that does not match the OpenAPI specification throws `ResponseValidationError` instead of being returned as it is. `ScheduleClient.getLog()` returns `ScheduleInvoked[]` rather than `string`, which is what the endpoint has always answered with.
Contributor
|
See more at https://github.com/apify/apify-client-js/actions/runs/33656440345#summary-100336057352 |
vdusek
added a commit
to apify/apify-docs
that referenced
this pull request
Aug 31, 2026
The OpenAPI spec now describes what the API actually returns: nullable `generalAccess` on storages and runs (and not required on `Run`), nullable `readme`/`input`/`changelog` in `ActorDefinition`, task `input` as an object or an array of objects, the private user fields optional on `UserPrivateInfo`, and `format: date-time` on `DailyServiceUsages.date`. These are the six deviations the JS client had to widen its generated zod schemas for in apify/apify-client-js#1016. *✍️ Drafted by Claude Code*
Member
|
The date parsing changes reminded me of #522, we should open this topic on slack in some public channel and agree on the direction. |
Contributor
Author
Sure, but I guess it does not block this PR, right? And we can do it later as a separate PR. |
Member
|
Yeah, that should be resolved separately, including an entry in the upgrading guide. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes: #1027
Description
This PR adds runtime validation of API responses using Zod schemas generated from the OpenAPI specification, similar to how the Python API client validates responses with Pydantic.
It follows up on #985, which introduced generated TypeScript types via openapi-typescript.
Schema generation
pnpm generate:modelsnow generates both:src/generated/api.ts— TypeScript typessrc/generated/schemas.ts— Zod schemasregenerate_models.yamlworkflow regenerates both.scripts/schema_emitter.mts.z.date()fordate-time, becauseparseDateFields()runs before validation,I also considered existing generators - hey-api, orval, kubb. None of them supports all the behaviors above natively, so the custom code would not disappear; it would move into resolvers or post-processing. Since our specification uses only a small subset of JSON Schema and all 240 generated schemas are cross-checked at compile time against the independently generated TypeScript types, keeping the emitter local seems like a better solution, and thanks to that to also avoid another dependency.
Validation
parseResponse()now unwraps the API response, parses date fields, and validates the result. All resource methods in the base clients go through it.ResponseValidationError, which includes:Tests
test/mock_server/fixtures.ts), with their validity checked infixtures.test.ts.Breaking changes
ResponseValidationError.ScheduleClient.getLog()now returnsScheduleInvoked[]instead ofstring, matching the actual API response.TaskPublicConfignow follows the specification:publishedAtis optional and read-only,categorizationis removed.All breaking changes are documented in the v3 upgrading guide.
Bundle size
The browser bundle grows from 288 kB to 327 kB due to the generated schemas. The limit in
rsbuild.config.tsis increased to 360 kB.✍️ Drafted by Claude Code