I have really enjoyed using TypeScript in all my node projects, and I like enforcing static type safety in all my applications. However, static typing doesn’t validate data at runtime, leaving your application vulnerable to unexpected inputs. This is where Zod, a TypeScript-first schema validation library, shines and I started to make use of it on frontend React applications and backend, express, AWS Lambdas applications.
Zod helps validate and parse incoming data while ensuring it adheres to predefined schemas. It’s intuitive, lightweight, and designed specifically for TypeScript users. It can validate API responses, user input, or configurations.
Why Zod?
- TypeScript-First: Zod generates static TypeScript types directly from schemas.
- Runtime Validation: Validates data at runtime, ensuring it conforms to your defined schema.
- Rich Ecosystem: Offers powerful features like object schemas, union types, and transformations.
- Simplicity: No boilerplate or extra dependencies. Zod is intuitive and easy to integrate.
Getting Started
Installation
You can install Zod via npm or yarn:
npm install zod
# or
yarn add zod
Defining Schemas
With Zod, you define schemas to validate your data. Here’s an example:
import { z } from "zod";
const userSchema = z.object({
name: z.string(),
age: z.number().int().positive(),
email: z.string().email(),
isAdmin: z.boolean().optional(), // Optional field
});
// Sample data
const userData = {
name: "John Doe",
age: 30,
email: "john.doe@example.com",
};
// Validate data
const parsedData = userSchema.parse(userData);
console.log(parsedData);
In this example:
- The z.object method defines an object schema.
- Fields like name and age are validated for their types and constraints.
- The .parse() method validates the userData object and throws an error if it doesn’t match the schema.
Error Handling
Zod provides detailed error messages when validation fails. Here’s an example:
try {
userSchema.parse({
name: "John Doe",
age: -5, // Invalid age
email: "invalid-email", // Invalid email
});
} catch (e) {
console.error(e.errors);
}
Output:
[
{ "code": "too_small", "minimum": 1, "type": "number", "message": "Value should be greater than 0", "path": ["age"] },
{ "code": "invalid_string", "validation": "email", "message": "Invalid email", "path": ["email"] }
]
Using TypeScript Types
Zod schemas automatically infer TypeScript types. Use the z.infer, this is really helpful once the schema has been defined. utility to extract types:
type User = z.infer<typeof userSchema>;
const user: User = {
name: "Jane Doe",
age: 25,
email: "jane.doe@example.com",
};
Advanced Features
Transformations
Zod can transform input data while validating it:
const stringToNumberSchema = z.string().transform((str) => parseInt(str));
const result = stringToNumberSchema.parse("42");
console.log(result); // 42 (as a number)
Union Types
Define schemas that accept multiple valid types using z.union:
const statusSchema = z.union([z.literal("success"), z.literal("failure")]);
statusSchema.parse("success"); // Passes
statusSchema.parse("unknown"); // Throws an error
Array Validation
Zod can validate arrays of items:
const numberArraySchema = z.array(z.number());
numberArraySchema.parse([1, 2, 3]); // Passes
numberArraySchema.parse(["1", "2", "3"]); // Throws an error
Nested Objects
You can validate deeply nested objects using z.object:
const nestedSchema = z.object({
user: z.object({
name: z.string(),
address: z.object({
street: z.string(),
city: z.string(),
}),
}),
});
nestedSchema.parse({
user: {
name: "Alice",
address: {
street: "123 Main St",
city: "Wonderland",
},
},
}); // Passes
Custom Validation
Create custom validation logic using .refine:
const passwordSchema = z
.string()
.min(8)
.refine((val) => /[A-Z]/.test(val), { message: "Must contain an uppercase letter" })
.refine((val) => /[0-9]/.test(val), { message: "Must contain a number" });
passwordSchema.parse("Password1"); // Passes
passwordSchema.parse("password"); // Throws an error
Integration Examples
With Express.js
Validate request data in an Express middleware:
import express from "express";
import { z } from "zod";
const app = express();
app.use(express.json());
const createUserSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number().int().positive(),
});
app.post("/users", (req, res) => {
try {
const userData = createUserSchema.parse(req.body);
res.status(200).send(userData);
} catch (e) {
res.status(400).send(e.errors);
}
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
With React
Validate form data in a React component:
import React, { useState } from "react";
import { z } from "zod";
const formSchema = z.object({
name: z.string(),
email: z.string().email(),
});
function App() {
const [formData, setFormData] = useState({ name: "", email: "" });
const [errors, setErrors] = useState<string | null>(null);
const handleSubmit = () => {
try {
formSchema.parse(formData);
alert("Form submitted successfully!");
} catch (e) {
setErrors(e.errors);
}
};
return (
<div>
<input
type="text"
placeholder="Name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
/>
<input
type="email"
placeholder="Email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
/>
<button onClick={handleSubmit}>Submit</button>
{errors && <pre>{JSON.stringify(errors, null, 2)}</pre>}
</div>
);
}
export default App;
I hope you got some useful information from this article, I have been using Zod in my code base and its intuitive, and TypeScript-first library for schema validation and runtime type checking.
Whether you’re building APIs, processing user input, or working on complex configurations, Zod provides a way to ensure your data conforms to expectations.
Its ability to infer TypeScript types, handle transformations, and offer detailed error messages makes Zod a must-have tool for any TypeScript project. To have more professional code base this library can eliminate many common runtime errors that are hard to catch when the applications get very large, and also if you are working in teams its a good way to make sure you have some validation standards and policies in your code base
I have included sample project as well on github:
https://github.com/EmiRoberti77/zod_ts_validation
Emi Roberti - Happy Coding
Top comments (0)