openapi-ts icon indicating copy to clipboard operation
openapi-ts copied to clipboard

Zod plugin

Open ZilvinasAbr opened this issue 1 year ago • 33 comments

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

ZilvinasAbr avatar Aug 07 '24 10:08 ZilvinasAbr

Sure would! Let's keep this issue open to gauge the interest and will prioritise accordingly

mrlubos avatar Aug 07 '24 12:08 mrlubos

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

jacobdr avatar Aug 07 '24 20:08 jacobdr

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

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

ZilvinasAbr avatar Aug 08 '24 09:08 ZilvinasAbr

I agree, it would be super helpful to have the zod schemas generated for scenarios with non-js backends.

RobertOstermann avatar Aug 08 '24 21:08 RobertOstermann

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 avatar Aug 13 '24 04:08 mrlubos

@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 avatar Aug 15 '24 01:08 vyacheslav-korneev

@vyacheslav-korneev Definitely go with custom, I don't do ETAs for now

mrlubos avatar Aug 15 '24 06:08 mrlubos

love to see this in next release, excellent work dear @mrlubos 👏 👏 👏

1saifj avatar Aug 17 '24 15:08 1saifj

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 avatar Aug 17 '24 16:08 mrlubos

@mrlubos idk i just love all in one stuffs xD

1saifj avatar Aug 17 '24 16:08 1saifj

We will get there my friend, it's just a question of priorities and what can have the biggest impact

mrlubos avatar Aug 17 '24 17:08 mrlubos

@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

ZilvinasAbr avatar Aug 19 '24 15:08 ZilvinasAbr

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 avatar Aug 30 '24 19:08 jasonlor

@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 avatar Aug 30 '24 19:08 mrlubos

@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

jasonlor avatar Aug 30 '24 20:08 jasonlor

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.

AdamDorwart avatar Sep 20 '24 02:09 AdamDorwart

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

thoroc avatar Oct 28 '24 08:10 thoroc

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!

mrlubos avatar Nov 19 '24 13:11 mrlubos

Phenomenal, @mrlubos. Thank you for your work!

ZilvinasAbr avatar Nov 19 '24 16:11 ZilvinasAbr

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

hongfanmeng avatar Nov 20 '24 13:11 hongfanmeng

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

mrlubos avatar Nov 20 '24 19:11 mrlubos

Added list of remaining tasks as of now https://github.com/hey-api/openapi-ts/issues/1320 🏃

mrlubos avatar Nov 21 '24 12:11 mrlubos

This is very cool and I'm excited for when it'll be ready! 🔥 Thanks for saving us time and making this stuff easy. ❤️

TylerSmith-SkapaTech avatar Dec 07 '24 14:12 TylerSmith-SkapaTech

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 avatar Jan 19 '25 02:01 EricDotSmith

@EricDotSmith what's your OpenAPI spec?

mrlubos avatar Jan 19 '25 03:01 mrlubos

@EricDotSmith what's your OpenAPI spec?

"openapi": "3.0.0",

EricDotSmith avatar Jan 19 '25 03:01 EricDotSmith

@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 avatar Jan 19 '25 10:01 mrlubos

@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.

CFrez avatar Jan 28 '25 18:01 CFrez

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

ArthMosHoppen avatar Feb 22 '25 06:02 ArthMosHoppen

@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'.

ghost avatar Apr 07 '25 17:04 ghost