Zod is a schema validation library. Its goes well with building Typescript projects. Zod has zero dependencies which means that you can install and use Zod without any other libraries, and it will help you keep your bundle size less bulky.
In this article we will cover how can you use a basic to custom validation logic for your schema parsing validation using Zod. This will just cover some basic way to add a custom validation logic in your schema created.
Why need a schema validation?
Schema validation is the process of verifying the data structure to a pre defined set of schema specification. We can use it to ensure the validity of input data, as well as to document and enforce your application’s data structures.
We might need to add a parsing library to our application mainely for the reasons below
- Parse form data before sending to the server
- Parse data before sending back out to the client
- Parse data in functions around our application
- Parse data at the API boundary before sending to the DB
- Parse data from an third-party data source
Zod makes it extremely easy to create simple schemas to validate primitives, objects, type inference from the schemas, composing schemas and more.
Installing
We will start a barebones project with the following commands.
mkdir zod-custom-validation
cd zod-custom-validation
npm init -y
npm i zod
npm i tsx --save-dev
After we have install all the needful packages to get start with typescript compilation we need to tweak this to our package.json file
"scripts": {
"validation": "tsx watch validation.ts"
}
Here we specify we will be running a file name validation.ts which we will be creating to experiment with zod validation.
Let's create a validation.ts file in our root folder with commands below
touch validaton.ts
npm run validaton
Basic Validation
To start with a basic validation we could start with having a User fields. This would something like
id would be a type of number
name would be a string
portfolio would be a string and should be a url
salary would be a number and is nullable/optional
import z from 'zod';
const schema = z.object({
id: z.string({
required_error: "ID is required",
invalid_type_error: "ID must be a string"
}),
name: z.string({
required_error: "Name is required",
invalid_type_error: "Name must be a string"
}),
portfolio: z.string({
required_error: "portfolio is required",
invalid_type_error: "portfolio must be a string"
}).url(),
salary: z.number({
invalid_type_error: "salary must be a number"
}).optional(),
})
type ValidatePayload = z.infer<typeof schema>
const payload: ValidatePayload = {
id: "35625",
name: "New user",
portfolio: "https://dev.to/isnan__h/custom-schema-validation-in-typescript-with-zod-5cp5"
}
const parseSchema = (props: ValidatePayload) => {
return schema.parse(props)
}
const result = parseSchema(payload)
console.log(JSON.stringify(result))
In the above snippet we defined a schema with a set of rules. These rules must me passed to execute our validation error free.
To check this we can pass a url that is not a URL then it would fail with a error needs portfolio to be a URL.
Custom Validation
To add a custom validation can be achieved with method provided to us .superRefine() and we have the .refine() method, The refine method is used to provide custom validation logic via refinements.
const schema = z.object({
name: z.string({
required_error: "Name is required",
invalid_type_error: "Name must be a string"
}),
phone: z.number({
required_error: "Phone number is required",
invalid_type_error: "Phone must be a number"
}),
confirmPhone: z.number({})
}) .refine((data) => data.phone === data.confirmPhone, {
message: "Phone numbers dont match"
});
const payload: ValidatePayload = {
name: "New user",
phone: 24562454453465,
confirmPhone: 24562454453465
}
const result = parseSchema(payload)
console.log(JSON.stringify(result))
This is a basic implementation of how we can add custom logic using the refine() method. In the above example we are making sure the phone and confirmPhone needs to be both same.
Conclusion
We could make the use of Zod to verify the schema we define in our application. These primitives can be chained together to build a very robust and flexible schema design for your application.
Having a runtime validation is always a needed thing for our application, as it makes our code more readable and sanitizes user inputs before redirecting them to the server.
For more information on Zod, be sure to check out the excellent documentation on their site.
You can find there more detailed examples of how to use all of the functionality that Zod has to offer.
https://zod.dev/?id=table-of-contents
https://zod.dev/?id=error-handling
Happy coding!
Top comments (2)
Very good timing on this post. The last few days I have begun using zod against graphql and providing safety going into my Vue props using the schema and infer