asteasolutions / zod-to-openapi

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

Can't extend Zod objects that have already declared openapi #276

Open vacas5 opened 13 hours ago

vacas5 commented 13 hours ago

Take the following code example

import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
import { z } from "zod";

extendZodWithOpenApi(z);

const BodySchema = z.object({ name: z.string() }).openapi({ title: "User", description: "Required user information" });
const QuerySchema = BodySchema.extend({ age: z.number().optional() }).openapi({
  title: "User details",
  description: "Optional user information",
});
const ParamsSchema = z.object({ id: z.string() });
const ResponseSchema = z.object({ success: z.boolean() });

export { BodySchema, ParamsSchema, QuerySchema, ResponseSchema };

As of version 5, trying to extend the BodySchema (which already utilized openapi) fails with:

Exception during run: TypeError: Cannot read properties of undefined (reading 'extendedFrom')
  at ZodObject.result.extend (/Users/~/express-zod-openapi-autogen/node_modules/@asteasolutions/zod-to-openapi/dist/index.cjs:84:106)

This may be intended behavior but I did not find it documented and it seemed to work prior to version 5. I have tested that this is still an issue in v6 and v7.

The problem seems to have been introduced to changes to the extendZodWithOpenApi function introduced here in the src/zod-extensions.ts file

Prior to this change, schemas could be extended and the new values declared in the openapi argument would override the originals.

vacas5 commented 13 hours ago

I've got a pull request open demonstrating the issue in another project: https://github.com/Foundry376/express-zod-openapi-autogen/pull/17/files

vacas5 commented 13 hours ago

A workaround here is to simply declare a zod type without using the openapi method and then extend that like so:


import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
import { z } from "zod";

extendZodWithOpenApi(z);

const BaseSchema = z.object({ name: z.string() });

const BodySchema = BaseSchema.openapi("Body", { title: "User", description: "Required user information" });
const QuerySchema = BaseSchema.extend({ age: z.number().optional() }).openapi({
  title: "User details",
  description: "Optional user information",
});
const ParamsSchema = z.object({ id: z.string() });
const ResponseSchema = z.object({ success: z.boolean() });

export { BodySchema, ParamsSchema, QuerySchema, ResponseSchema };```

But I wanted to at least flag it as it worked without issue prior to v5.