asteasolutions / zod-to-openapi

A library that generates OpenAPI (Swagger) docs from Zod schemas
MIT License
788 stars 52 forks source link

Error: 'Cannot convert undefined to a BigInt' when using coerce. #208

Closed langaads closed 4 months ago

langaads commented 5 months ago

When using a schema that coerces a bigint, it throws an exception. removing the coerce works.

"dependencies": {
    "@asteasolutions/zod-to-openapi": "^6.3.1",
    "zod": "^3.22.4"
}
TypeError: Cannot convert undefined to a BigInt
    at BigInt (<anonymous>)
    at ZodBigInt._parse (/workspace/node_modules/zod/lib/types.js:1080:26)
    at ZodBigInt._parseSync (/workspace/node_modules/zod/lib/types.js:129:29)
    at ZodBigInt.safeParse (/workspace/node_modules/zod/lib/types.js:159:29)
    at ZodBigInt.isOptional (/workspace/node_modules/zod/lib/types.js:316:21)
    at OpenAPIGenerator.isOptionalSchema (/workspace/node_modules/@asteasolutions/zod-to-openapi/dist/openapi-generator.js:588:26)
    at /workspace/node_modules/@asteasolutions/zod-to-openapi/dist/openapi-generator.js:605:45
    at Array.filter (<anonymous>)
    at OpenAPIGenerator.requiredKeysOf (/workspace/node_modules/@asteasolutions/zod-to-openapi/dist/openapi-generator.js:605:14)
    at OpenAPIGenerator.toOpenAPIObjectSchema (/workspace/node_modules/@asteasolutions/zod-to-openapi/dist/openapi-generator.js:611:31)

Node.js v20.11.0

Minimal reproduction script: node index.js

const {
  OpenAPIRegistry,
  OpenApiGeneratorV3,
} = require("@asteasolutions/zod-to-openapi");
const { z } = require("zod");

const $coercedBigInt = z.object({
  id: z.coerce.bigint(),
});

const registry = new OpenAPIRegistry();
registry.registerPath({
  method: "get",
  path: "/test",
  responses: {
    200: {
      content: { "application/json": { schema: $coercedBigInt } },
    },
  },
});

const generator = new OpenApiGeneratorV3(registry.definitions);
const docs = generator.generateDocument({ openapi: "3.0.3" });

console.log(JSON.stringify(docs, null, 2));
AGalabov commented 4 months ago

Hey @langaads that is a very interesting case. However I do not think this has anything to do with our library.

I traced what happens in our code since we never call parse/safeParse. And I actually got to this

const testSchema = z.coerce.bigint();

console.log(testSchema.isOptional());

The isOptional getter of zod is throwing the error. And there is nothing we can do about that - I feel like this is a perfectly valid case and it is a bug on zod's end.

With a quick look up I can see that there is an open issue there - https://github.com/colinhacks/zod/issues/1911

I am closing this issue on our end since there is not much that we can do in my opinion

langaads commented 4 months ago

Thanks @AGalabov for the feedback and research on our behalf, at least this issue will help as a documentation to anyone having the same problem. Using the schema.isOptional() is probably not a common usecase when handling zod schemas alone.

For now i'm hacking my way trough removing the coerce before passing schemas to the docs, but it's a laborous work as i use snowflake Ids for most my entities, and i need them coerced since they are strings on incoming jsons.

const coercedBigInt = z.object({
  id: z.coerce.bigint(),
  other: z.any(),
});

const coercedBigIntDocs = coercedBigInt
  .omit({ id: true })
  .extend({ id: z.bigint() });