hey-api / openapi-ts

✨ Turn your OpenAPI specification into a beautiful TypeScript client
https://heyapi.vercel.app
MIT License
634 stars 44 forks source link

Question - Using generated schemas with a tool like ajv #458

Open maulik13 opened 2 months ago

maulik13 commented 2 months ago

Hi, I am really interested in the schema generation part since it includes the shape and contraints on the fields. My first thought was to plug in AJV together with the schema objects that openapi-ts generates. Which is proving to be a challenge since the schema includes JSON references (e.g. $ref: "#/components/schemas/CategoryTypeDto") from my OpenApi specs.

Here is the generated schema JavaScript object by openapi-ts which includes JSON references.

export const $EmailTenantRegistration = {
  required: ["tenant", "user"],
  type: "object",
  properties: {
    user: {
      $ref: "#/components/schemas/UserRegistrationInfo",
    },
    tenant: {
      $ref: "#/components/schemas/TenantRegisterationInfo",
    },
  },
  additionalProperties: false,
} as const;

And here is the snippet from my open API spec under "components" -> "schemas" ->

"EmailTenantRegistration": {
  "required": [
    "tenant",
    "user"
  ],
  "type": "object",
  "properties": {
    "user": {
      "$ref": "#/components/schemas/UserRegistrationInfo"
    },
    "tenant": {
      "$ref": "#/components/schemas/TenantRegisterationInfo"
    }
  },
  "additionalProperties": false
},

Should openapi-ts resolve these references when creating JavaScript objects? AJV cannot resolve these references from the schemas.gen.ts file.

mrlubos commented 2 months ago

I think so! We need to find out what else people use these schemas for, but that sounds like a reasonable feature. However, since these schemas may create a pretty deeply nested tree, we would need to think about the impact this change could have on generation performance, object size, etc

maulik13 commented 2 months ago

Yes nesting needs to be handled carefully. If you are open to contributions, I can help draft a PR.

mrlubos commented 2 months ago

Totally @maulik13, but would love to discuss the approach first. Do you already know how you'd go about implementation?

maulik13 commented 2 months ago

Sure! Let me go through the code a bit more and I can create a simple proposal to see how this can be solved.

mrlubos commented 2 months ago

cc @jordanshatford

samvaughton commented 2 months ago

I am actually looking at AJV myself to take the OpenAPI / JSON Schema functionality to its optimal level (auto sdk + validation).

Should openapi-ts resolve these references when creating JavaScript objects? AJV cannot resolve these references from the schemas.gen.ts file.

You potentially don't need to nest the schemas and explore that complexity of recursive types, AJV can handle looking up the references provided - see here: https://ajv.js.org/guide/combining-schemas.html#combining-schemas-with-ref. It just needs its $id field.

I have tried this myself and it works well. The issue I encountered is that the $ref looks for the $id of the schema. And since I am working with a contract that is on 3.0.3 (hoping to upgrade soon - C# ecosystem is slow) the use of $id is not allowed. In 3.1 anything under schema is fully JSON Schema compliant. So for my testing I manually added in $id to the schema objects.

For 3.1 adding all the individual schemas to AJV as long as an $id has been provided will work as-is.

For 3.0, it might be that the $id is generated automatically for the schemas themselves to reference each other.

mrlubos commented 2 months ago

Hey @samvaughton don't see your contact on your profile but found you on LinkedIn, would love to chat more about this

maulik13 commented 2 months ago

@samvaughton That is a very good suggestion, but unfortunately it will not work in my case. I am using ajv together with react-form-hooks which I believe does not take multiple schemas as parameters. In addition, I am also stuck with pre 3.1 version of schema as I am in the same boat as you using dotnet backend.

mrlubos commented 2 months ago

If you have any proposals how to fix this, we can plan a solution. I definitely intend to get around to this, just might take a while as there are many other feature requests as you can imagine

samvaughton commented 2 months ago

Yes it seem the only way is a custom resolver (on the react forms side) https://medium.com/@vinjenks/react-custom-validation-resolver-with-react-hook-form-7a7c3ec96827

maulik13 commented 2 months ago

In order to find a solution, the main question here is how the schema objects will be used and what version of JSON schema specs are we targetting.

For my use case, library would not need any changes when OpenApi.Net library supports OpenApi version 3.1 and $id field is properly populated (which it should) by openapi-ts. Then, I can potentially use a custom resolver with ajv as suggested by @samvaughton. So, this particular case is covered.

But, I do see a need to resolve $ref within component schemas when OpenApi spec is lower than 3.1 and $id is not included. Should we then find a way to convert OpenApi 3.0.3 schema to a version of JSON Schema that gives more meaningful output (e.g. by adding $id)?

mrlubos commented 2 months ago

Since dereferencing schemas would introduce a great deal of complexity, my vote would be for bringing pre-3.1 schemas to 3.1, i.e. JSON Schema 2020-12. While this too can become complex, this complexity would be limited to the codegen itself and not the output.

We could also ask people to upgrade to OpenAPI 3.1, though not everyone might be able to do that. Thoughts?

maulik13 commented 1 month ago

Sorry for inactivity from my side, due to time constraint I decided not to use the schema with ajv. I am using zod and just using the maxLength and minLength from the schema.

But, in general I agree that pre-3.1 schemas can be brought to 3.1 given that it does not break anyone's usecase replying on 3.0 OpenApi version output. I don't know how that can be determined though. One way to solve this could be to provide an option that can be set to upgrade the schema automatically for pre-3.1 OpenApis.

mrlubos commented 1 month ago

@maulik13 Are you using anything to generate Zod types/schemas?

maulik13 commented 1 month ago

I am generating zod schemas by hand right now. But, would be awesome to generate them :) However, one challenge is that the actual UI schemas have some special needs. Like, mapping lists (e.g. users) to selection boxes, have custom validations on the UI side (to provide more detailed feedback). So for that reason, I have settled in generating them manually.

I have been evaluating different libraries for generating zod, but none have landed properly with me yet. But this library is providing me with services and types for calling services and that has been working great so far. And I am excitingly waiting for the React Query integration!