Recipe
This recipe shows how to validate data structures using Zod, including synchronous and asynchronous validation patterns.
Zod version 3.22+ is supported, which provides the zod/v3 export path required by libraries like json-schema-to-zod.
Code example
Synchronous validation
import { z } from "zod";
// Define the Contract schema
const ContractSchema = z.object({
contractId: z.string().uuid("Invalid contract ID format"),
clientName: z.string().min(1, "Client name is required"),
contractValue: z.number().min(0, "Contract value must be non-negative"),
status: z.union([
z.literal("active"),
z.literal("completed"),
z.literal("terminated"),
]),
startDate: z.string().datetime("Invalid start date format"),
endDate: z.string().datetime("Invalid end date format").optional(),
});
// Infer TypeScript type from schema
type Contract = z.infer<typeof ContractSchema>;
// Example data
const validContract = {
contractId: "550e8400",
clientName: "Client X",
contractValue: 50000,
status: "active" as const,
startDate: "2024-01-01T00:00:00Z",
endDate: "2024-12-31T23:59:59Z",
};
const invalidContract = {
contractId: "invalid-uuid",
clientName: "",
contractValue: -1000, // invalid: negative value
status: "active" as const,
startDate: "2024-01-01T00:00:00Z",
};
// Synchronous validation with safeParse()
const result1 = ContractSchema.safeParse(validContract);
if (!result1.success) {
console.error("Validation errors:", result1.error.format());
} else {
console.log("✓ Valid contract:", result1.data);
}
// Handling validation errors
const result2 = ContractSchema.safeParse(invalidContract);
if (!result2.success) {
console.error("Validation errors:", result2.error.format());
} else {
console.log("Valid contract:", result2.data);
}
// Alternative: parse() method (throws on error)
try {
const validated = ContractSchema.parse(validContract);
console.log("\n✓ Parsed successfully:", validated);
} catch (error) {
if (error instanceof z.ZodError) {
console.error("Validation failed:", error.issues);
}
}
Asynchronous validation
import { z } from "zod";
const asyncContract = {
contractId: "660e8400",
clientName: "Client Y",
contractValue: 75000,
status: "active" as const,
startDate: "2024-06-01T00:00:00Z",
};
// Asynchronous validation with refinements
const AsyncContractSchema = ContractSchema.extend({
clientName: z.string().refine(
async (val) => {
// Simulate database check for duplicate client
await new Promise((resolve) => setTimeout(resolve, 100));
return val !== "Client X"; // Fail if client already has active contract
},
{ message: "Client already has an active contract" }
),
});
AsyncContractSchema.safeParseAsync(asyncContract)
.then((result) => {
if (!result.success) {
console.error("✗ Async validation errors:", result.error.format());
} else {
console.log("✓ Valid contract (async):", result.data);
}
})
.catch((err) => console.error("Unexpected error:", err));
safeParse() vs parse()
safeParse(): Returns a result object with success boolean. Safe for unknown data.
parse(): Throws ZodError on validation failure. Use with try-catch blocks.
TypeScript SDK
Learn about the Intuned Browser SDK.