This page looks best with JavaScript enabled

TypeScript schema validation with Zod

 ·  ☕ 7 min read  ·  ✍️ Iskander Samatov

TypeScript schema validation with Zod


This post will describe setting up schema validation for your project using Zod . Zod is an open-source TypeScript library for schema declaration and validation. We will look at why to use Zod for schema validation, provide examples of how to use it, and compare it with other libraries.

What is schema validation, and why do you need it?

Schema validation is the process of verifying that a data structure conforms to a specific schema. You can use it to ensure the validity of input data, as well as to document and enforce your application’s data structures.

There are two main benefits to using schema validation:

  • Data integrity at the runtime: Ensuring that data is entered into your system in the correct format can help avoid errors and maintain data consistency. While TypeScript can help you ensure type safety at a compile-time, schema validation shines at runtime when you’re dealing with data coming from unknowns such as server or user input.
  • Documentation: A good schema validation library will provide accurate type definitions for the data structures you use. Type definitions can be used to generate static documentation for your project.

Why use Zod?

While there are many TypeScript schema validation libraries out there, Zod is one of the best. When choosing a library for your project, you should consider its implementation details in addition to its feature set.

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 smaller.

Zod works in Node.js and all major browsers (including IE 11). This makes it a good choice for projects that need to support a wide range of platforms.

Zod is designed to be TypeScript-first, meaning that the library will automatically infer the static TypeScript type for your data structures. This eliminates the need to declare types twice - once in Zod and again in TypeScript. It will save you a lot of typing and will help you keep your code changes in sync.

Zod’s API is concise and chainable. This makes it easy to create complex data schemas. You can also easily compose simpler schemas into more complex data ones.

Example of using Zod for schema validation

Primitives

Let’s start off with the simplest example - primitives. Zod makes it extremely easy to create simple schemas to validate primitives.

Let’s see how we would validate a string. Let’s say we want to validate a password that the user inputs. We want the password to be a non-empty string that’s at least 8 characters long and a maximum of 32 characters long:

1
2
3
4
5
    import { z } from "zod";
    const stringSchema = z.string().nonempty().min(8).max(32);
    stringSchema.parse("");
    stringSchema.parse(""); // throws an exception
    stringSchema.parse("I am a valid password"); // returns "I am a valid password"

When you run the code above, you’ll see that the parse method throws an exception. The exception will contain an array of ZodError objects with a detailed description of the error:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    [
      {
      "code": "too_small",
      "minimum": 1,
      "type": "string",
      "inclusive": true,
      "message": "Should be at least 1 characters",
      "path": []
      },
      {
      "code": "too_small",
      "minimum": 8,
      "type": "string",
      "inclusive": true,
      "message": "Should be at least 8 characters",
      "path": []
      }
    ]

When you try parsing a valid string, zorse will simply return its value.

Objects

Now let’s move on to objects. Zod has powerful support for validating nested object structures.

Let’s create a schema to validate a user object. It will contain the following fields:

  • name
  • email
  • phoneNumber

To declare the schema, we use the z.object() method:

1
2
3
4
5
6
    import { z } from "zod";
    const User = z.object({
      email: z.string().email(),
      name: z.string(),
      phoneNumber: z.number()
    });

Let’s try validating a sample object against the schema we just created:

1
2
3
4
    const result = User.parse({
      email: "hi@sample.com",
      name: "Hello World"
    });

The parse method will return an object containing the result of parsing. Since we forgot to provide the phoneNumber field in our example, Zod will throw an exception with the following array of errors:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    [
      {
      "code": "invalid_type",
      "expected": "number",
      "received": "undefined",
      "path": [
        "phoneNumber"
      ],
      "message": "Required"
      }
    ]

Type inference

We can also infer the type from the schema object. This is the part where you can get type definitions for your schemas for free and use them in your app:

1
    type UserType = z.infer<typeof User>;

Composing schemas

Zod makes it easy to compose complex schemas on top of one another, just like you would build legos.

To demonstrate this, let’s use our User schema from above and build up a more detailed user object that has a hobby:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    const User = z.object({
      email: z.string().email(),
      name: z.string(),
      phoneNumber: z.number()
    });
    const Hobby = z.object({
      hobbyName: z.string().min(1)
    });
    const Hobbyist = User.merge(Hobby);
    const result = Hobbyist.safeParse({
      email: "hi@sample.com",
      name: "Hello World",
      phoneNumber: 123
    });

By combining two of our schemas, we created a new one that you can use to validate that a user object has a proper hobby field.

Zod has a number of other useful methods for composing and combining schemas that you can find here.

Composing new schemas on top of the existing ones is a great approach because it helps you keep all of the changes in your data structures in sync.

Caveats

There are a couple of things to keep in mind when validating with Zod.

Safe parsing

If you don’t want Zod to throw an exception, when parsing fails, you can use the safeParse method instead. This will return an object that contains the parsing result:

1
2
    mySchema.safeParse(""I am a valid password""); // => { success: true; data: "I am a valid password" }
    mySchema.safeParse(""); // => { success: false; error: ZodError }

Unrecognized keys are stripped out

By default, Zod schemas strip out unrecognized keys during parsing. This means that any unknown keys will be ignored.

If you want to pass through unrecognized keys without losing them, you can use the .passthrough() method.

Order matters

The .array() method returns a new ZodArray instance which means the order in which you call methods matters. By switching the order of the calls in the chain, you could get a completely different schema result:

1
2
    z.string().optional().array(); // (string | undefined)[]
    z.string().array().optional(); // string[] | undefined

Comparison of Zod with other libraries

Other widely-used schema validation libraries are also a good choice, like yup and io-ts.

Here are some of the reasons why Zod might be a better choice for your project:

  • TypeScript first support. Zod was built with TypeScript in mind and has first-class support. This means that you can get autocompletion and excellent VsCode support.
  • Type inference for free - get types for your schemas without extra work.
  • Easily composable schemas - build up complex validation rules by combining different schemas.
  • Robust error handling. Zod has excellent error handling baked in, with a rich API for configuring your error handling flow.
  • Support for Promise and function schemas. If you need to validate a function or Promise, Zod has you covered.

Conclusion

In this post, we covered how to use the Zod library for TypeScript schema validation. We looked at how to create schemas and use schemas to validate data structures. We also saw some of the things to watch out for when using Zod and the advantages it has over other libraries.

For more information on Zod, be sure to check out the excellent documentation on their Github page. There you can find detailed examples of how to use all of the functionality that Zod has to offer.

If you’d like to get more web development, React and TypeScript tips consider following me on Twitter, where I share things as I learn them.
Happy coding!

Share on

Software Development Tutorials
WRITTEN BY
Iskander Samatov
The best up-to-date tutorials on React, JavaScript and web development.