Overview
The ez object provides specialized Zod schemas for common API patterns like dates, file uploads, forms, and raw data.
import { ez } from "express-zod-api";
import { z } from "zod";
Date Schemas
ez.dateIn()
Accepts ISO date strings and provides Date objects to your handler.
const endpoint = defaultEndpointsFactory.build({
input: z.object({
startDate: ez.dateIn(),
endDate: ez.dateIn({ examples: ["2024-12-31"] }),
}),
handler: async ({ input }) => {
// input.startDate and input.endDate are Date objects
const days = daysBetween(input.startDate, input.endDate);
return { days };
},
});
Supported formats:
2021-12-31T23:59:59.000Z
2021-12-31T23:59:59Z
2021-12-31T23:59:59
2021-12-31
ez.dateOut()
Accepts Date objects from your handler and returns ISO strings in responses.
const endpoint = defaultEndpointsFactory.build({
output: z.object({
createdAt: ez.dateOut(),
updatedAt: ez.dateOut({ examples: ["2024-01-01T00:00:00Z"] }),
}),
handler: async () => {
return {
createdAt: new Date(),
updatedAt: new Date("2024-01-01"),
};
},
});
File Upload
ez.upload()
Handles file uploads with multipart/form-data.
const uploadEndpoint = defaultEndpointsFactory.build({
method: "post",
input: z.object({
avatar: ez.upload(),
document: ez.upload(),
}),
output: z.object({ success: z.boolean() }),
handler: async ({ input }) => {
// input.avatar: { name, mv(), mimetype, data, size, etc }
await input.avatar.mv(`/uploads/${input.avatar.name}`);
return { success: true };
},
});
File object properties:
MIME type (e.g., “image/png”)
Move file to destination: mv(path: string) => Promise<void>
True if file exceeded size limit
Handles URL-encoded form data (application/x-www-form-urlencoded).
const formEndpoint = defaultEndpointsFactory.build({
method: "post",
input: ez.form({
name: z.string().min(1),
email: z.string().email(),
subscribe: z.enum(["true", "false"]).transform(v => v === "true"),
}),
output: z.object({ success: z.boolean() }),
handler: async ({ input }) => {
await saveContact(input);
return { success: true };
},
});
Raw Data
ez.raw()
Accepts raw request body as Buffer for binary data.
import { ez } from "express-zod-api";
const rawEndpoint = defaultEndpointsFactory.build({
method: "post",
input: ez.raw({
examples: [{ data: Buffer.from("example").toString("base64") }],
}),
output: z.object({ length: z.number() }),
handler: async ({ input: { data } }) => {
// data is a Buffer
return { length: data.length };
},
});
ez.buffer()
For documenting binary responses in OpenAPI.
const fileResultHandler = new ResultHandler({
positive: { schema: ez.buffer(), mimeType: "application/pdf" },
// ...
});
ez.paginated()
Creates reusable pagination schemas.
import { ez } from "express-zod-api";
import { z } from "zod";
const pagination = ez.paginated({
style: "offset", // or "cursor"
itemSchema: z.object({
id: z.number(),
name: z.string(),
}),
itemsName: "users",
maxLimit: 100,
defaultLimit: 20,
});
const listUsers = defaultEndpointsFactory.build({
input: pagination.input,
output: pagination.output,
handler: async ({ input: { limit, offset } }) => {
const users = await db.users.find().limit(limit).skip(offset);
const total = await db.users.count();
return { users, total, limit, offset };
},
});
Offset-based (limit/offset):
// Input: { limit?: number, offset?: number }
// Output: { items: T[], total: number, limit: number, offset: number }
Cursor-based (limit/cursor):
// Input: { limit?: number, cursor?: string }
// Output: { items: T[], nextCursor: string | null, limit: number }
Complete Example
import { defaultEndpointsFactory, ez } from "express-zod-api";
import { z } from "zod";
const createEventEndpoint = defaultEndpointsFactory.build({
method: "post",
input: z.object({
title: z.string().min(1).max(200),
description: z.string(),
startDate: ez.dateIn(),
endDate: ez.dateIn(),
image: ez.upload().optional(),
}),
output: z.object({
id: z.string(),
createdAt: ez.dateOut(),
imageUrl: z.string().url().nullable(),
}),
handler: async ({ input }) => {
let imageUrl = null;
if (input.image) {
const filename = `${uuid()}.${ext(input.image.name)}`;
await input.image.mv(`./uploads/${filename}`);
imageUrl = `https://cdn.example.com/${filename}`;
}
const event = await db.events.create({
title: input.title,
description: input.description,
startDate: input.startDate,
endDate: input.endDate,
imageUrl,
});
return {
id: event.id,
createdAt: event.createdAt,
imageUrl,
};
},
});
See Also