Schema Validation with Zod and Express.js

Table of contents

What is Zod?

Zod is a TypeScript-first schema declaration and validation library, Created by Colin McDonnell. Unlike Yup, Zod is TypeScript-first which means it has a lot of features for TypeScript developers.

Zod comes with some really great features like:

  • Works in Node.js and browsers (including IE 11)
  • Zero dependencies
  • Works with JavaScript too

Why do you need to validate your API Calls?

Validating your API Calls helps you get the right data that you want, For example, you want your users to have a strong password(e.g. at least 6 characters), You can use something like Zod or Yup and prevent users from entering a short password(less than 6 characters). Also, doing validation on the server makes your server much more secure, Because no one can open the developer tools, go through your code and figure out how to beat your validation.

Let's start coding

First, Create an empty directory and navigate into it:

mkdir schema-validation-with-zod-and-expressjs
cd schema-validation-with-zod-and-expressjs

Then, Initialize a Node.js project and add the necessary dependencies:

npm init -y
npm install express zod

Next, add the following script to our package.json file.

package.json
{
  // ...
  "scripts": {
    "dev": "node index.js"
  },
  // ...
}

Now let's start an Express.js server. Create a file called index.js at the root of the project:

index.js
const express = require("express");

const app = express();

app.use(express.json());

app.listen(1337, () => console.log(`> Ready on http://localhost:${1337}`));

Then run the Express.js server(You can access it at http://localhost:1337).

npm run dev

Next, we can start working with Zod, Let's first import z from zod and add a simple login schema.

index.js
const express = require("express");
const { z } = require("zod");

const app = express();

app.use(express.json());

const LoginSchema = z.object({
  // In this example we will only validate the request body.
  body: z.object({
    // email should be valid and non-empty
    email: z.string().email(),
    // password should be at least 6 characters
    password: z.string().min(6),
  }),
});

// ...

Now we are going to create our middleware for Zod validation.

index.js
// ...

const validate = (schema) => (req, res, next) => {
  try {
    schema.parse({
      body: req.body,
      query: req.query,
      params: req.params,
    });

    next();
  } catch (err) {
    return res.status(400).send(err.errors);
  }
};

// ...

Finally, we are going to create a route(/login) for POST requests, which we will use our middleware(validate) to perform the validation of the request body.

index.js
// ...

// pass LoginSchema to validate middleware
app.post("/login", validate(LoginSchema), (req, res) => {
  return res.json({ ...req.body });
});

// ...

The final code would be as follows:

index.js
const express = require("express");
const { z } = require("zod");

const app = express();

app.use(express.json());

const LoginSchema = z.object({
  // In this example we will only validate the request body.
  body: z.object({
    // email should be valid and non-empty
    email: z.string().email(),
    // password should be at least 6 characters
    password: z.string().min(6),
  }),
});

const validate = (schema) => (req, res, next) => {
  try {
    schema.parse({
      body: req.body,
      query: req.query,
      params: req.params,
    });

    next();
  } catch (err) {
    return res.status(400).send(err.errors);
  }
};

app.post("/login", validate(LoginSchema), (req, res) => {
  return res.json({ ...req.body });
});

app.listen(1337, () => console.log(`> Ready on http://localhost:${1337}`));

Conclusion

In this guide, We learned how to validate our Express.js REST API Calls using Zod, You can find the code on GitHub.

Make sure to contact me if you have any questions.