Skip to main content

Command Palette

Search for a command to run...

Introducing Zod: A Developer-Friendly Schema Declaration and Validation Library

Published
7 min read
Introducing Zod: A Developer-Friendly Schema Declaration and Validation Library
M

I'm Mihir, a Full-Stack Web Developer and open-source enthusiast currently pursuing my sophomore year at RIT

When working with TypeScript, managing and validating data types can often become repetitive and cumbersome. That's where Zod comes in. Zod is a TypeScript-first schema declaration and validation library that simplifies the process by eliminating the need for duplicative type declarations.

What is Zod?

Zod is a powerful tool designed to make developers' lives easier. When we talk about a "schema" with Zod, we're referring to any data type, whether it's a simple string or a complex nested object. With Zod, you declare a validator once, and it automatically infers the static TypeScript type. This approach not only reduces redundancy but also ensures that your types and validations are always in sync.

Why Use Zod?

1. Zero Dependencies

Zod is lightweight and doesn't rely on any other libraries. This means fewer dependencies to manage and a leaner project overall.

2. Cross-Environment Compatibility

Whether you're working in Node.js or modern browsers, Zod works seamlessly across environments, providing consistent performance and functionality.

3. Tiny Footprint

With a minified and zipped size of just 8kb, Zod won't bloat your project. Its small size ensures fast load times and efficient resource use.

4. Immutability

Zod's methods, like .optional(), return a new instance instead of modifying the existing one. This immutability leads to safer and more predictable code.

5. Concise, Chainable Interface

Zod's API is designed to be intuitive and easy to use. Its chainable methods allow you to build complex schemas with clear and readable code.

6. Functional Approach

Zod emphasizes a functional approach with methods like parse, which parse your data rather than simply validating it. This approach can lead to cleaner and more maintainable code.

7. Plain JavaScript Support

You don't need to use TypeScript to benefit from Zod. It works perfectly with plain JavaScript too, making it versatile and accessible for various projects.

Primary components of Zod

There are primarily components in Zod

  • Schema: A schema defines the structure and rules for the data. In Zod, you create schemas using z.object(), z.string(), z.number(), etc. It's your blueprint for what valid data looks like.

  • Validation: This is the process of checking if the data matches the schema. It helps ensure that the data adheres to the defined rules.

  • Parsing: This is the process of converting data to the expected format. Even if data comes in a different format, Zod helps transform it into the format you need.

Let's now see how we can use Zod, step by step

Installing Zod

To get started, you need to install Zod in your project. You can do this using npm, which is pretty straightforward. Just run the following command in your terminal:

npm install zod

It's as easy as adding a new book to your shelf!

Creating Schemas in Zod

Zod schemas define the structure of your data. Here’s a simple example of how to create a schema:

const { z } = require('zod');

const userSchema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email"),
  password: z.string().min(6, "Password must be at least 6 characters long"),
});

In this example, we define a schema for a user, ensuring they have a valid name, email, and password length. It’s like setting rules for a game—everyone must follow them to have a good time!

Validating Data with Zod

Once you have defined a schema, you can use it to validate data. Here's how it works:

try {
  const userData = {
    name: "John Doe",
    email: "john.doe@example.com",
    password: "secret123"
  };

  const validatedData = userSchema.parse(userData);
  console.log("Valid data:", validatedData);
} catch (error) {
  console.error("Validation error:", error.errors);
}

If the userData doesn’t meet the criteria we set in our userSchema, Zod will throw an error, alerting us to what's wrong. It's like getting a friendly reminder that not everything is right—time to check those details!

Handling Validation Errors

Zod throws detailed errors when validation fails, which you can catch and handle appropriately. This helps in providing meaningful error messages to the users. Instead of generic errors that can be confusing, Zod gives specific feedback—making it easier to pinpoint what's gone wrong, much like a helpful teacher who provides clear feedback on your assignments.

Integration with Express.js

Zod can be easily integrated with Express.js to validate request data in your API endpoints. This ensures that all incoming data conforms to the expected format before processing it. By doing this, you can significantly reduce the chances of running into unexpected bugs due to invalid data. It's like checking a guest’s invitation before letting them into your party—ensuring that everyone belongs!

Mini-Project with Express.js and MongoDB

Let's dive deeper into the mini-project where we'll create a user management system using Express.js, Zod, and MongoDB.

Step 1: Set Up the Project

Create a new directory and initialize it:

mkdir express-zod-mongo
cd express-zod-mongo
npm init -y

Start crafting your project’s home!

Step 2: Install Dependencies

Install the necessary packages:

npm install express zod mongoose dotenv

Setting up the right environment is key to a successful project!

Step 3: Project Structure

Organize your project as follows:

express-zod-mongo/
├── .env
├── index.js
├── models/
│   └── User.js
└── routes/
    └── user.js

Keeping things tidy makes it easier to find your way around!

Step 4: Environment Variables

Create a .env file to store your MongoDB connection string:

MONGODB_URI=mongodb://localhost:27017/express_zod_mongo

This way, you keep sensitive information private and easily adjustable.

Step 5: Set Up Mongoose and Express

Create index.js to set up the server and connect to MongoDB:

require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');

const app = express();
const port = 3000;

// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
}).then(() => {
  console.log('Connected to MongoDB');
}).catch((error) => {
  console.error('Error connecting to MongoDB:', error);
});

app.use(express.json());

// Import routes
const userRoutes = require('./routes/user');
app.use('/api/users', userRoutes);

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

Your Express server is getting ready on port 3000!

Step 6: Create the User Model

Define the user schema with Mongoose in models/User.js:

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
});

const User = mongoose.model('User', userSchema);

module.exports = User;

This model sets the layout for how user data is stored in your MongoDB database.

Step 7: Create User Routes

Set up the routes in routes/user.js:

const express = require('express');
const { z } = require('zod');
const User = require('../models/User');

const router = express.Router();

// Zod schema for user validation
const userSchema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email"),
  password: z.string().min(6, "Password must be at least 6 characters long"),
});

// Create a new user
router.post('/', async (req, res) => {
  try {
    const validatedData = userSchema.parse(req.body);
    const user = new User(validatedData);
    await user.save();
    res.status(201).json(user);
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json(error.errors);
    } else {
      res.status(500).json({ message: 'Internal server error' });
    }
  }
});

// Get all users
router.get('/', async (req, res) => {
  try {
    const users = await User.find();
    res.status(200).json(users);
  } catch (error) {
    res.status(500).json({ message: 'Internal server error' });
  }
});

module.exports = router;

These routes allow users to enter their information and also retrieve existing users—a handy feature for any user management system!

Step 8: Run the Server

Make sure your MongoDB server is running, then start your Express server:

node index.js

Your server should now be running on http://localhost:3000. It's showtime!

Testing the API

  1. Create a new user:

     curl -X POST http://localhost:3000/api/users -H "Content-Type: application/json" -d '{
       "name": "John Doe",
       "email": "john.doe@example.com",
       "password": "secret123"
     }'
    
  2. Get all users:

     curl http://localhost:3000/api/users
    

This project demonstrates how to use Zod for data validation in an Express.js application, ensuring that all user inputs are correctly formatted before they are saved to MongoDB. By integrating Zod into your project, you’ll create a smoother and more reliable user experience, helping to catch issues early and improve data quality!

Conclusion

Zod is an excellent choice for developers looking to simplify their schema declaration and validation processes. Its developer-friendly design, minimal footprint, and functional approach make it a standout tool in the TypeScript ecosystem. Whether you're dealing with simple data types or complex nested structures, Zod has you covered.

Ready to make your TypeScript code cleaner and more efficient? Give Zod a try!