Zod can be used in many situations, including
When you don't trust the inputs to your application
For example, if you're building a CLI, the input is unknown at runtime.
For inputs you trust but don't control
For example, if your app relies on a third-party API, you can use Zod to validate the API.
Getting user data in forms
Schema validation confirms that data adheres to a standard defined by the developer.
Sending data to APIs
Schema validation confirms that data adheres to a standard defined by the developer.
>> mkdir zodExamples && cd zodExamples
>> npm init -y
>> npm -i D typescript ts-node zod @types/node
>> git init # optional
>> npx tsc --init
>> echo "node_modules/" >> .gitignore
>> mkdir src && touch src/index.ts
Edit package.json
file. File should look something like this,
{
"name": "zodtut",
"version": "1.0.0",
"description": "",
"main": "./dist/index.js",
"scripts": {
"build": "tsc -b",
"start": "ts-node src/index.ts",
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"keywords": [],
"author": "Swapnil Narwade",
"license": "ISC",
"devDependencies": {
"@types/node": "^20.12.7",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"zod": "^3.23.4"
}
}
Red background marked are the changes; we want in our in package.json
file.
tsconfig.json
file and do the following.
"rootDir": "./src/"
"outDir"
: "./dist"
Now you’re ready to code 🎉
Edit index.ts
file we created inside src
folder. Populate using below code.
//src/index.ts
import { z } from "zod";
/** Let's create user schema for user registration form */
const userSchema = z.object({
username: z.string(),
password: z.number(),
confirmPasswrod: z.number(),
registerd: z.boolean().default(false),
})
/** Introduce errors: just add username and leave rest of the field */
const user = {
username: "Now_Tiger",
}
/** Read/validate data using zod */
console.log(userSchema.parse(user))
Run our application.
>> npm start
So you will get long list of errors as shown below.
ZodError: [
{
"code": "invalid_type",
"expected": "number",
"received": "undefined",
"path": [
"password"
],
"message": "Required"
},
{
"code": "invalid_type",
"expected": "number",
"received": "undefined",
"path": [
"confirmPassword"
],
"message": "Required"
}
]
...
issues: [
{
code: 'invalid_type',
expected: 'number',
received: 'undefined',
path: [Array],
message: 'Required'
},
{
code: 'invalid_type',
expected: 'number',
received: 'undefined',
path: [Array],
message: 'Required'
}
],
addIssue: [Function (anonymous)],
addIssues: [Function (anonymous)],
errors: [
{
code: 'invalid_type',
expected: 'number',
received: 'undefined',
path: [Array],
message: 'Required'
},
{
code: 'invalid_type',
expected: 'number',
received: 'undefined',
path: [Array],
message: 'Required'
}
]
}
So on above error message Zod required the password
field of type number or integer and same goes with confirmPassword
.
Although you could just log user
variable without the Zod parsing
, you will not get any errors, although this article is all about data validations.
//src/zstring.ts
import { z } from "zod";
const stringSchema = z.string();
const username = 56;
const result = stringSchema.safeParse(username);
if (!result.success) {
console.log(result.error); // Long list of errors
} else {
// do something
console.log(result.data);
}
// run program using
// ts-node src/zstring.ts
//src/znumber.ts
import { z } from "zod";
const ageSchema = z.number().gt(1).nonnegative({ message: "age shouldn't be negative" });
const userAge: number = 27;
const res = ageSchema.safeParse(userAge);
if (!res.success) {
console.log(res.error.issues);
} else {
console.log(res.data);
if (res.data >= 25) {
console.log("You're eligible for the subscription 🎉");
} else {
console.log("Eligible age for the subscription is 25 and greater!");
}
}
// run program using
// ts-node src/znumber.ts
Let’s see what other validation features Zod provides.
Let’s say we want our users to set their password of length greater than 5 characters & same goes for confirmPassword
field.
And let’s put default value of registered
to false
.
Edit index.ts
file as below.
//src/index.ts
import { z } from "zod";
const userSchema = z.object({
username: z.string(),
password: z.number().gt(5),
confirmPassword: z.number().gt(5),
registered: z.boolean().default(false),
});
const user = {
username: "Now_Tiger",
password: 22345678, // put password of length 3
confirmPassword: 22453456, // try confirm password of length 3 as well and print data
};
console.log(userSchema.parse(user));
Let’s create one file called logger.ts
inside src
folder and populate with below code.
We will be creating our own data structure rather a schema for our company’s employees.
//src/logger.ts
import { z } from "zod";
const employeeSchema = z.object({
id: z.number().min(4).nonnegative({ message: "ID should be a positive number only" }),
name: z.string().max(50).min(5),
email: z.string().email({ message: "Invalid email addresss" }),
});
try {
const manager = {
id: -1010, // introduced an error; length is correct but introduced a negative number
name: "Swapnil",
email: "swapnilnarwadegmail.com", // invalid email; missed @ symbol & see how powerful zod is;
};
employeeSchema.parse(manager);
} catch (error) {
if (error instanceof z.ZodError) {
console.log(error.issues);
}
}
Run this script by entering ts-node src/logger.ts
command on your terminal ensuring that you’re at the root level of this project.
[
{
code: 'too_small',
minimum: 4,
type: 'number',
inclusive: true,
exact: false,
message: 'Number must be greater than or equal to 4',
path: [ 'id' ]
},
{
code: 'too_small',
minimum: 0,
type: 'number',
inclusive: true,
exact: false,
message: 'ID should be a positive number only',
path: [ 'id' ]
},
{
validation: 'email',
code: 'invalid_string',
message: 'Invalid email addresss',
path: [ 'email' ]
}
]
Hope you have got some better error result on your terminal, hopefully readable enough. Still we can improve the readability of errors, Zod provides additional methods to do so.
Edit logger.ts
file as shown below. Basically edit the try-catch block as shown/marked below.
//src/logger.ts
try {
const manager = {
id: -1010, // introduced error here
name: "Swapnil",
email: "swapnilnarwadegmail.com", // invalid email
};
employeeSchema.parse(manager);
} catch (error) {
if (error instanceof z.ZodError) {
console.log(error.flatten());
}
}
You should be getting something similar to below error output, after running the script.
{
formErrors: [],
fieldErrors: {
id: [
'Number must be greater than or equal to 4',
'ID should be a positive number only'
],
email: [ 'Invalid email addresss' ]
}
}