Transform input data during validation with Zod schemas
Transformations allow you to modify data as it flows through validation. This is especially useful for converting string parameters to numbers, parsing dates, normalizing data, and more.
Use .transform() to convert validated data into a different format:
import { z } from "zod";const getUserEndpoint = endpointsFactory.buildVoid({ input: z.object({ id: z.string().transform((id) => parseInt(id, 10)), }), handler: async ({ input: { id }, logger }) => { logger.debug("id", typeof id); // number // id is now a number, not a string },});
You can chain validators before and after transformations:
import { z } from "zod";import { defaultEndpointsFactory } from "express-zod-api";const updateUserEndpoint = defaultEndpointsFactory.build({ method: "patch", input: z.object({ id: z .string() .regex(/^\d+$/, "ID must be numeric") .transform((id) => parseInt(id, 10)) .refine((id) => id >= 0, "ID must be non-negative"), }), output: z.object({ success: z.boolean(), }), handler: async ({ input }) => { // id is validated as string, transformed to number, then validated as number return { success: true }; },});
Here’s a complete example from the Express Zod API source showing transformation in action:
import { z } from "zod";import { ez, defaultEndpointsFactory } from "express-zod-api";const updateUserEndpoint = defaultEndpointsFactory.build({ method: "post", input: z.object({ // Path parameter arrives as string id: z .string() .example("12") .transform((value) => parseInt(value, 10)) .refine((value) => value >= 0, "should be greater than or equal to 0"), name: z.string().nonempty(), birthday: ez.dateIn(), // Transforms ISO string to Date }), output: z.object({ name: z.string(), createdAt: ez.dateOut(), // Transforms Date to ISO string }), handler: async ({ input }) => { // input.id is a number // input.birthday is a Date return { name: input.name, createdAt: new Date("2022-01-22"), }; },});