Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[zod-openapi] - OpenAPI 3.0: the usage of nullish and nullable result in "type" null #211

Open
RobbyUitbeijerse opened this issue Jul 1, 2024 · 6 comments

Comments

@RobbyUitbeijerse
Copy link

RobbyUitbeijerse commented Jul 1, 2024

Hi there!

Our team started using the latest version of the packages after the merger of #206 (thanks for that!)

We noticed a small issue (but quite consequential) when it comes to nullable properties. I'll try to explain what we're facing in an example, but also dropped a full diff of our spec after migrating to @anatine/zod-openapi. I'm currently assuming it's an issue only when using patchNestjsSwagger with 3.0.

Here's a schema:

export const ContinuationTokenSchema = z.object({
  continuation: z.string().nullish().default(null),
});

This schema results in the following OpenAPI output:

"continuation": {
   "default": null,
   "type": "null"
},

While we are actually expecting it to be:

"continuation": {
   "type": "string"
   "default: null, 
   "nullable": true
},

It happens for all sorts of schema: arrays, booleans, strings - below you will find a link to our OpenAPI spec after migrating to @anatine/zod-openapi

(left isnestjs-zod, right is @anatine/zod-openapi)
https://www.diffchecker.com/Zg4nfCdt/

@RobbyUitbeijerse RobbyUitbeijerse changed the title [zod-openapi] the usage of nullish and nullable result in "type" null [zod-nestjs] the usage of nullish and nullable result in "type" null Jul 1, 2024
@RobbyUitbeijerse
Copy link
Author

RobbyUitbeijerse commented Jul 1, 2024

I did an additional test with generateSchema from @anatine/zod-openapi, it indeed seems to be the 3.0 option.

Here's that same schema I mentioned with generateSchema, both with the 3.0 as well as the 3.1 version specified

import { generateSchema } from '@anatine/zod-openapi';
import { z } from 'zod';

const schema = z.object({
  continuation: z.string().nullish().default(null),
});

const resultA = generateSchema(schema, undefined, '3.0');
// {"type":"object","properties":{"continuation":{"default":null,"type":"null"}}}

const resultB = generateSchema(schema, undefined, '3.1');
// {"type":["object"],"properties":{"continuation":{"default":null,"type":["string","null"]}}}

What I think is happening, is that string() and nullish() are both processed in order, but since nullish() comes after string, the ZodNullable overrides the type property that came in ZodString


Update
Locally I applied a patch by adjusting parseNullable like below, but I'm not that into it to estimate whenever this will cause any issues for the 3.1 implementation, or 3.0 edge cases.

function parseNullable({
  schemas,
  zodRef,
  useOutput,
  openApiVersion,
}: ParsingArgs<z.ZodNullable<OpenApiZodAny>>): SchemaObject {
  const schema = generateSchema(zodRef.unwrap(), useOutput, openApiVersion);
  return merge(
    schema,
    {
      ...(openApiVersion === '3.0' && {
        nullable: true
      }),
    },
    zodRef.description ? { description: zodRef.description } : {},
    ...schemas
  );
}

@RobbyUitbeijerse RobbyUitbeijerse changed the title [zod-nestjs] the usage of nullish and nullable result in "type" null [zod-opneapi] - OpenAPI 3.0: the usage of nullish and nullable result in "type" null Jul 1, 2024
@RobbyUitbeijerse RobbyUitbeijerse changed the title [zod-opneapi] - OpenAPI 3.0: the usage of nullish and nullable result in "type" null [zod-openapi] - OpenAPI 3.0: the usage of nullish and nullable result in "type" null Jul 1, 2024
@EgorPopovPP
Copy link

EgorPopovPP commented Jul 15, 2024

@RobbyUitbeijerse There is my result from generation 3.1 spec

GetUserBloggerInfoResponse: {
    "type": "object",
    "properties": {
        "inn": {
            "type": "null"
        },
        "organization": {
            "type": "null"
        },
        "signatoryFio": {
            "type": "null"
        },
        "signatoryPosition": {
            "type": "null"
        },
        "signatoryActsOn": {
            "type": "null"
        },
        "markingUserId": {
            "type": "null"
        },
        "rsUrl": {
            "type": "null"
        }
    }
}

Zod

export const UserBloggerInfoSchema = z.object({
  inn: z.string().nullable(),
  organization: z.string().nullable(),
  signatoryFio: z.string().nullable(),
  signatoryPosition: z.string().nullable(),
  signatoryActsOn: z.string().nullable(),
  markingUserId: z.string().nullable(),
  rsUrl: z.string().nullable(),
})

@dani-mp
Copy link

dani-mp commented Jul 24, 2024

Saw the same today as well:

const zodSchema = z.object({ a: z.string().nullable() })
const openAPISchema = generateSchema(zodSchema, false, '3.0')
{
  "type": "object",
  "properties": {
    "a": {
      "type": "null"
    }
  },
  "required": ["a"]
}

@anle-statsig
Copy link

any update on fixing this? Is this also only for this verion?

@MarkusAbtion
Copy link

Bump

@ConnorSinnott
Copy link

Howdy! I'm not too familiar with the inner workings of these libraries, our stack only had @anatine/zod-nestjs^2.0.9 installed and we were running into this issue. I was able to get things working by additionally installing @anatine/zod-openapi:2.2.1 (version specific) which overrode the peer-dependency requirement of zod-nestjs. Looking forward to having this patched so I can unlock the version of zod-openapi.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants