Why Write Validation Logic When Zod Can Do It Better?
Learn how to strengthen your TypeScript applications with Zod, a powerful schema validation library. Discover how Zod enables type-safe, runtime validation with minimal boilerplate, so you can save time and effort when developing API services and data-driven applications.
Zod is a powerful, TypeScript-first validation library that combines static type safety with runtime data validation. Itβs especially useful when you want to ensure that incoming data matches your expected structure without writing repetitive boilerplate code.
Contents
Safe Validation with Result Types
If you prefer non-throwing validation, use safeParse
. It returns a result object that separates success from failure:
const validation = UserSchema.safeParse(user);
if (validation.error) {
console.error(validation.error.format());
} else {
const user = validation.data;
console.log(user.name);
}
This approach is useful in functional programming patterns, where you handle errors as data instead of using exceptions. It can be used for always returning Result Types.
Composing Zod Schemas
Zod supports reusable and composable schemas, allowing you to build complex validations from smaller building blocks:
import { z } from 'zod';
const AddressSchema = z.object({
street: z.string(),
city: z.stringw(),
state: z.string(),
postalCode: z.string(),
country: z.string(),
});
export const UserSchema = z.object({
address: AddressSchema,
name: z.string().default('John Doe'),
});
Schemas like AddressSchema
can be reused across multiple models, improving maintainability and consistency. The Zod validator is written in TypeScript itself and also supports features like Literal types and readonly properties.
Saving Time with Zod
Using Zod for this not only improves type safety and maintainability but also makes validation much easier and more reliable compared to manual checks.
Validation without Zod
In plain TypeScript, you're responsible for manually checking for missing properties using conditionals. You also need to define fallback values on your own and validate data types explicitly, which can quickly become tedious when working with nested data structures:
type User = {
name: string;
age: number;
};
async function fetchUser(): Promise<User> {
const res = await fetch('https://api.example.com/user');
const data = await res.json();
if (!data.age) {
throw new Error(`User has no age`);
}
return {
name: data.name || 'John Doe',
age: data.age,
};
}
This kind of defensive programming can quickly become repetitive, error-prone, and difficult to maintain as your data structures grow in size and complexity.
Validation with Zod
Zod provides a much cleaner and declarative way to define and validate data shapes. It automatically infers the TypeScript type from the schema, reducing boilerplate and keeping your code DRY:
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().default('John Doe'),
age: z.number(),
});
async function fetchUser() {
const res = await fetch('https://api.example.com/user');
const data = await res.json();
return UserSchema.parse(data);
}