elysiajs / elysia-swagger

A plugin for Elysia to auto-generate Swagger page
MIT License
74 stars 36 forks source link

Support for nullable property in OpenAPI 3 #86

Open Droidion opened 6 months ago

Droidion commented 6 months ago

I have model defined like

t.Object({
  numericProp: t.Nullable(t.Number()),
})

This generates expected type TUnion<[TNumber, TNull]>.

And in OpenAPI I'm getting

"numericProp": {
  "anyOf": [
    {
      "type": "null"
    },
    {
      "type": "number"
    }
  ]
}

OpenAPI spec says

OpenAPI 3.0 does not have an explicit null type as in JSON Schema, but you can use nullable: true to specify that the value may be null. Note that null is different from an empty string "".

So, the expected OpenAPI schema would be

"numericProp": {
  "type": "null",
  "nullable": true
}

SwaggerUI also has problem rendering model example, saying "numericProp": "Unknown Type: null",

bostjanpisler commented 6 months ago

workaround I'm using for null

t.Object({ numericProp: t.Union([t.Number(), t.Null()]) })

Mudbill commented 6 months ago

Came to report this same issue. You'd think that t.Nullable(t.String()) would generate the correct type for the UI. Instead it appears as this:

image image

Reading the spec, it appears that OpenAPI v3.1 uses a union of null and X to represent this (which is correctly done), but maybe the included version of SwaggerUI doesn't support 3.1.

Maybe this is simply a mismatch of the expected OpenAPI version between @elysiajs/swagger and elysia itself? (since t comes from the main package)

Mudbill commented 6 months ago

Okay so I discovered something in plain sight that actually seems to have a big impact.

Since the null union is a OpenAPI 3.1 feature (not 3.0), it is of course important that SwaggerUI is parsing 3.1. However, the default seems to be 3.0.

Default and incorrect setup

Notice how it says "OAS 3.0" in the green badge up top of the web page. ```ts const app = new Elysia() .use(swagger()) .post("/", () => "Hello Elysia", { body: t.Object({ nullableString: t.Nullable(t.String()), }), }) .listen(3000); ``` image

Changing the OpenAPI version for Swagger seems to make it handle the correct types that elysia.t generates.

Corrected version matching

```ts const app = new Elysia() .use( swagger({ documentation: { openapi: "3.1.0", // Add this }, }) ) .post("/", () => "Hello Elysia", { body: t.Object({ nullableString: t.Nullable(t.String()), }), }) .listen(3000); ``` image image

I think the suggested fix here is to use 3.1.0 as the default version for Swagger unless otherwise specified, to be in sync with what the default format is from the schema generation. But perhaps there's some important reason why 3.0.3 is used instead that I'm not aware of.

Droidion commented 6 months ago

Yes, @Mudbill has the proper good workaround, worked for me. I also agree, that maybe 3.1.0 should be default version, and/or docs can say something about recommended version.