Since upgrade to Zod v4, examples expects transformed type and not input type. #2962
-
Descriptionimport { defaultEndpointsFactory } from "express-zod-api";
import z from "zod";
export const getProductEndpoint = defaultEndpointsFactory.build({
method: "get",
tag: "product",
description: "Get a product by id.",
input: z.object({
product_id: z.string().transform(Number).pipe(z.number().int().min(0)).example("1"),
}),
output: z.object({
product_id: z.number(),
}),
handler: async ({ input: { product_id } }) => {
await new Promise(resolve => setTimeout(resolve, 1000));
return { product_id };
},
}); Expected
Context
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
Hello @JonathanCabezas , this is not a bug, it was a required breaking change to adopt Zod 4 in version 24. Here is a quote from the Changelog:
There was also a migration advice for the case like yours: input: z.string()
+ .example("123")
.transform(Number)
- .example("123")
It's valid depending on location. |
Beta Was this translation helpful? Give feedback.
-
This is a bummer. I was using some helper zod objects such as: export const greaterThanZeroIntFromStringSchema = z
.string()
.transform(Number)
.pipe(z.number().int().min(0)); Obviously I don't want the same value for my examples, which I use to generate a SwaggerUI doc automatically, for example: const PaginationSchema = z
.object({
limit: greaterThanZeroIntFromStringSchema
.optional()
.describe("The number of items per page to return"),
offset: greaterThanZeroIntFromStringSchema
.optional()
.describe("The page wanted, starting from 0"),
})
.example({
limit: "2", // <-- Not working anymore
offset: "1" ,// <-- Not working anymore
}); This is a feature regression as far as I'm concerned. |
Beta Was this translation helpful? Give feedback.
-
here you go, @JonathanCabezas : const makeGreaterThanZeroIntFromStringSchemaWithExample = (example: string) => z
.string()
.example(example) // <—————
.transform(Number)
.pipe(z.number().int().min(0));
const PaginationSchema = z
.object({
limit: makeGreaterThanZeroIntFromStringSchemaWithExample("2")
.optional()
.describe("The number of items per page to return"),
offset: makeGreaterThanZeroIntFromStringSchemaWithExample("1")
.optional()
.describe("The page wanted, starting from 0"),
}); OR you can extract transformation and piping into such helper, but keep examples along with description: const makeNonNegative = (subject: z.ZodString) =>
subject.transform(Number).pipe(z.int().nonnegative());
makeNonNegative(z.string().describe("some").example("123")); |
Beta Was this translation helpful? Give feedback.
-
For complicated input types it gets really verbose, and hard to read: input: z.object({
product_id: makeGreaterThanZeroIntFromStringSchemaWithExample("1"),
brand: z.string().example("MyBrand"),
price: z.number().example("3.3")
}) instead of the way more readable: input: z.object({
product_id: greaterThanZeroIntFromStringSchema,
brand: z.string(),
price: z.number()
}).example({
product_id: "1",
brand: "MyBrand",
price: 3.3
}) Also I had a list of mock products I could just use in my code: input: z.object({
product_id: greaterThanZeroIntFromStringSchema,
brand: z.string(),
price: z.number()
}).example(inputExampleProducts[0]) I tried .meta(examples:[]) but I get no type safety. |
Beta Was this translation helpful? Give feedback.
here you go, @JonathanCabezas :
OR you can extract transformation and piping into such helper, but keep examples along with description:
c…