In the world of TypeScript and JavaScript development, data validation and schema management are crucial aspects of creating robust applications. One library that has been gaining attention for its powerful yet intuitive approach to schema validation is Zod. This tutorial will walk you through everything you need to know about Zod, from its basics to advanced features, with practical examples and comparisons.
Zod is a TypeScript-first schema declaration and validation library. It allows you to define schemas for your data and validate that your data conforms to these schemas. Zod provides a fluent API to build and validate schemas easily and integrates well with TypeScript’s static type checking.
Zod works by allowing you to define a schema for your data and then use that schema to validate data at runtime. Here’s a basic example to illustrate how Zod works:
import { z } from 'zod';
// Define a Zod schema
const userSchema = z.object({
name: z.string(),
age: z.number().int().positive(),
});
// Validate data against the schema
const result = userSchema.safeParse({ name: 'Alice', age: 30 });
console.log(result); // { success: true, data: { name: 'Alice', age: 30 } }
const invalidResult = userSchema.safeParse({ name: 'Alice', age: -5 });
console.log(invalidResult.error.format()); // Error due to age being negative
TypeScript provides static type checking, which is great for development-time type safety. However, it doesn’t handle runtime validation. This is where Zod comes in. While TypeScript ensures that your types are correct at compile time, Zod ensures that your data is valid at runtime.
Zod vs TypeScript
Ensures type safety at compile time. Types are erased at runtime, so there's no built-in mechanism to enforce data validity during execution.
Provides runtime validation to ensure that data adheres to the specified schema. This validation happens during execution and complements TypeScript’s compile-time checks.
Using Zod alongside TypeScript allows you to validate data thoroughly, combining the best of both worlds.
Zod schemas define the shape and constraints of your data. They are highly customizable and can be composed to handle complex data structures.
import { z } from 'zod';
// Define a basic schema
const schema = z.object({
name: z.string(),
age: z.number(),
});
// Validate data
const validationResult = schema.safeParse({ name: 'Bob', age: 25 });
console.log(validationResult); // { success: true, data: { name: 'Bob', age: 25 } }
const nestedSchema = z.object({
user: z.object({
name: z.string(),
age: z.number(),
}),
address: z.object({
city: z.string(),
postalCode: z.string(),
}),
});
const nestedResult = nestedSchema.safeParse({
user: { name: 'Alice', age: 30 },
address: { city: 'Wonderland', postalCode: '12345' },
});
console.log(nestedResult); // { success: true, data: ... }
Zod validation involves checking if the data conforms to the defined schema. Zod provides various methods to handle different validation scenarios.
const optionalSchema = z.object({
name: z.string().optional(),
age: z.number().optional(),
});
const resultWithOptionalFields = optionalSchema.safeParse({ name: 'John' });
console.log(resultWithOptionalFields); // { success: true, data: { name: 'John', age: undefined } }
import { z } from 'zod';
const schema = z.string().regex(/^[a-zA-Z0-9]+$/, 'Invalid input');
// Example usage
try {
schema.parse('valid123'); // Passes validation
schema.parse('invalid!@#'); // Throws error: Invalid input
} catch (e) {
console.error(e.errors); // Logs validation error
}
Transformations in Zod allow you to modify data as it is being validated. This is useful for data normalization or preprocessing.
const transformSchema = z.string().transform((val) => val.toUpperCase());
const transformedResult = transformSchema.safeParse('hello');
console.log(transformedResult); // { success: true, data: 'HELLO' }
const userSchemaWithTransform = z.object({
name: z.string().transform((val) => val.trim()),
age: z.number(),
});
const transformedUserResult = userSchemaWithTransform.safeParse({ name: ' Alice ', age: 25 });
console.log(transformedUserResult); // { success: true, data: { name: 'Alice', age: 25 } }
The refine method in Zod is used for custom validation logic that goes beyond the built-in constraints.
const ageSchema = z.number().refine((val) => val >= 18, {
message: 'Age must be at least 18',
});
const validAge = ageSchema.safeParse(25);
console.log(validAge); // { success: true, data: 25 }
const invalidAge = ageSchema.safeParse(16);
console.log(invalidAge.error.format()); // Error due to age being less than 18
The preprocess method allows you to modify the input data before it is validated.
const preprocessSchema = z.preprocess(
(input) => (typeof input === 'string' ? parseInt(input, 10) : input),
z.number()
);
const processedResult = preprocessSchema.safeParse('42');
console.log(processedResult); // { success: true, data: 42 }
const invalidResult = preprocessSchema.safeParse('not a number');
console.log(invalidResult.error.format()); // Error due to invalid number
Here are more practical examples of using Zod in various scenarios:
const ColorEnum = z.enum(['Red', 'Green', 'Blue']);
const colorResult = ColorEnum.safeParse('Red');
console.log(colorResult); // { success: true, data: 'Red' }
const invalidColorResult = ColorEnum.safeParse('Yellow');
console.log(invalidColorResult.error.format()); // Error due to 'Yellow' not being in the enum
const formSchema = z.object({
username: z.string().min(3, 'Username must be at least 3 characters long'),
password: z.string().min(6, 'Password must be at least 6 characters long'),
});
const formData = { username: 'user', password: 'pass123' };
const formResult = formSchema.safeParse(formData);
console.log(formResult); // { success: true, data: { ... } }
When it comes to performance, different libraries and methods have varying speeds. Zod is known for its efficiency and ease of use, but there are other libraries and techniques that might be faster depending on your needs.
Joi: Another popular validation library that is mature and feature-rich but can be slower in some cases compared to Zod.
Yup: Provides a similar feature set but might have different performance characteristics.
Custom Validators: Writing your own validation logic can sometimes be more performant if you have very specific requirements.
Zod is a powerful tool for handling schema validation in TypeScript applications. It offers a clear, concise API for defining schemas, validating data, and performing transformations. Whether you’re using Zod for basic data validation or complex scenarios involving enums and custom logic, it provides a robust solution that complements TypeScript’s type system.
By understanding and utilizing Zod’s features—such as schema definitions, validation methods, transformations, refinements, and preprocessors—you can enhance your application’s data integrity and maintainability.
Feel free to experiment with the examples provided and integrate Zod into your projects to improve data validation and overall application quality. Happy coding!
https://zod.dev/docs
https://www.typescriptlang.org/docs/handbook/enum.html
https://github.com/colinhacks/zod
https://www.typescriptlang.org/docs/
simplify and inspire technology
©2024, basicutils.com