Zod plugin
Description
Currently openapi-ts generates typescript definitions for the OpenAPI schema. Would it be possible to generate zod schemas also? Something similar like these libraries: https://github.com/astahmer/openapi-zod-client and https://github.com/astahmer/typed-openapi
Sure would! Let's keep this issue open to gauge the interest and will prioritise accordingly
We sort of go the other way -- we use fastify-type-provider-zod as the backbone to generate the OpenAPI schema. But I see a use case where you would want to generate the zod validators from the OpenAPI schema if you build one by hand, and then import those into a UI package for integration into something like react-hook-form and its zod validation provider
We sort of go the other way -- we use fastify-type-provider-zod as the backbone to generate the OpenAPI schema. But I see a use case where you would want to generate the zod validators from the OpenAPI schema if you build one by hand, and then import those into a UI package for integration into something like
react-hook-formand its zod validation provider
Correct. The situation I find myself is a non-js backend which generates OpenAPI schema and it would be nice to be able to get not only type definitions of the OpenAPI schema but also zod schemas
I agree, it would be super helpful to have the zod schemas generated for scenarios with non-js backends.
For people wanting to generate Zod schemas, do you need 1:1 schema matching the endpoint, so you can use it to validate request/response? Which one of those is higher priority?
@mrlubos In our case we'd like to validate server response to be sure that it complies with the schema that was provided.
Out of curiosity, when may this feature be available if you start working on it? Any ETAs so we can understand whether we need to go with any custom solution?
@vyacheslav-korneev Definitely go with custom, I don't do ETAs for now
love to see this in next release, excellent work dear @mrlubos 👏 👏 👏
Hey I said nothing about next release, don't try to bully me into shipping it that fast @1saifj 🤣 Would be good to collect some information first. I assume people here are already using other packages for generating Zod schemas such as those linked above. Why are those not sufficient/what benefit would you gain by having Hey API generate Zod schemas?
@mrlubos idk i just love all in one stuffs xD
We will get there my friend, it's just a question of priorities and what can have the biggest impact
@mrlubos Thanks for the question. I guess for my personal case, for starters it would be most beneficial to have schemas for all the OpenAPI resources/entities. E.g.
For resource/entity User there would also be a zod schema generated.
Also, I noticed that currently this library actually generates JSON Schema of what I just mentioned. I might first try to use some tool to convert those JSON schemas to zod schemas first 🤔
After that, it would be nice to have zod schemas for each endpoint payload and response, but that is probably secondary for now
Would be incredible to be able to generate a react query client that uses fetch API, with request/responses validated with zod from an openapi spec
@jasonlor I agree. Out of curiosity, why do you need to validate both? Do you not trust the API to uphold the OpenAPI contract?
@mrlubos Ah the intent is to help out with overall productivity. I have two developers who are working with each other (server/client) and sometimes encounters issues due to incorrectly formed requests or missing objects on the response side.
This would help enforce the interface and help streamline any unintended errors that might be present. Other benefits w/ react query would be from Tkdodo regarding type inference:
- https://tkdodo.eu/blog/type-safe-react-query
Here's some existing work that I dove into this morning that could be helpful to reference – https://github.com/mattpocock/zod-fetch
For now I'll be generating the zod schemas from the types.gen.ts file using https://github.com/fabien0102/ts-to-zod
And the manually defining the hooks with the recently released react-query plugin
Just wanted to throw another use case in the pool. I have a Python backend that generates an OpenAPI schema. I would love to:
- Generate a client
- Generate Tanstack Query keys/functions
- Generate Zod schema's so I can use them in things like react-hook-form validation.
There's a case for I shouldn't couple input form validation with backend model definitions but sometimes it makes things easier. Zod isn't a requirement - I just want my models with validation rules available and zod is pretty good.
For people wanting to generate Zod schemas, do you need 1:1 schema matching the endpoint, so you can use it to validate request/response? Which one of those is higher priority?
That would definitely help as the AWS Powertools uses Zod for parsing events https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/parser/#built-in-schemas
Hey all, I haven't forgotten about you! Had to ship the experimental parser first, now full steam ahead on the Zod plugin (see WIP output). This plugin will be exclusive to the experimental parser, just like the recently released Fastify plugin. Thanks for your patience!
Phenomenal, @mrlubos. Thank you for your work!
For people wanting to generate Zod schemas, do you need 1:1 schema matching the endpoint, so you can use it to validate request/response? Which one of those is higher priority?
In my case, I used zod to validate request body from user input, with lib like React hook form
Still a ton of work to do, but why not make it available to you today?! Feedback is always good. For now it handles only reusable components, and even those are not fully done yet, see https://github.com/hey-api/openapi-ts/blob/main/packages/openapi-ts/test/snapshots/3.1.x/plugins/zod/default/zod.gen.ts
Nonetheless, you can now enable Zod by adding 'zod' to your plugins. Of course, more changes will be done before it's considered ready https://heyapi.dev/openapi-ts/zod.html
Added list of remaining tasks as of now https://github.com/hey-api/openapi-ts/issues/1320 🏃
This is very cool and I'm excited for when it'll be ready! 🔥 Thanks for saving us time and making this stuff easy. ❤️
I get the following as soon as I add zod to the plugins array 🤷🏼♂️
Maximum call stack size exceeded.
RangeError: Maximum call stack size exceeded.
@EricDotSmith what's your OpenAPI spec?
@EricDotSmith I'll need at least the relevant portion of the spec to recreate the issue if you want me to have a look!
@mrlubos Love this! I have run into a bug when parsing a regex:
Pydantic Schema
class Customer(BaseSchema):
first_name: str
last_name: str
email: EmailStr
phone: str = Field(pattern=r"^\d{3}[\s-]?\d{3}[\s-]?\d{4}$")
notes: str | None = None
Autogenerated
export const zCustomer = z.object({
first_name: z.string(),
last_name: z.string(),
email: z.string().email(),
phone: z.string().regex(/^\\d{3}[\\s-]?\\d{3}[\\s-]?\\d{4}$/),
notes: z.union([
z.string(),
z.null()
]).optional()
});
I found two ways that it works with react-hook-form:
phone: z.string().regex(/^\d{3}[\s-]?\d{3}[\s-]?\d{4}$/),
or
phone: z.string().regex(new RegExp("/^\\d{3}[\\s-]?\\d{3}[\\s-]?\\d{4}$/")),
Pretty sure this is a simple fix, but I did not test outside of react-hook-form.
Thank you for your work.
I have an unexpected behavior when i have some number field with positive validation :
page: z.number().positive().int()
In my openapi.json :
"page": { "type": "integer", "exclusiveMinimum": true, "minimum": 0 },
the result in zod.gen.ts is :
page: z.number().int().gt(true)
which raises an error :
Argument of type 'boolean' is not assignable to parameter of type 'number'.ts(2345)
thanks in advance
@mrlubos - finally getting around to changing ts-to-zod over to your plugin. I'm curious why you went with z.enum vs using z.nativeEnum and using the exported enums?
OpenAPI Schema:
"SearchScoreWorkflowState": {
"type": "string",
"enum": [
"pending",
"queued",
"running",
"success",
"failure"
],
"title": "SearchScoreWorkflowState"
},
zod.gen.ts
export const zSearchScoreWorkflowState = z.enum([
'pending',
'queued',
'running',
'success',
'failure',
])
types.gen.ts:
export enum SearchScoreWorkflowState {
PENDING = 'pending',
QUEUED = 'queued',
RUNNING = 'running',
SUCCESS = 'success',
FAILURE = 'failure',
}
If you used z.nativeEnum(SearchScoreWorkflowState) I wouldn't run into this in typescript:
Type '"pending" | "queued" | "running" | "success" | "failure"' is not assignable to type 'SearchScoreWorkflowState'.
Type '"pending"' is not assignable to type 'SearchScoreWorkflowState'.