Enhance Your Express Applications with Zod Library
Express.js is a great framework for building web applications. It offers flexibility, and ease of use whilst being very robust. In this article, we will use Zod to enhance the features of a web API built with Express.
Zod is a schema validation library, and it is a very good one. Schemas are structured blueprints that describe a type of data. Zod allows us to perform strict and passive validation on defined schemas, to ensure that our data conforms to the expected blueprint.
Zod can provide many useful features like data serialization and deserialization, extra static type checking for TypeScript, and can perform robust form validation, which we will cover in this article.
Form Validation
Form validation is important in API development. It improves security and prevents unexpected errors. Using Zod to perform form validation in Express is a simple process which we would go through below:
Scenario:
Imagine you have a user registration form with the following fields:
username
: Must be a string with a minimum length of 3 characters.email
: Must be a valid email address.password
: Must be at least 8 characters long.age
: Must be an optional field, but if provided, it must be a positive integer greater than or equal to 18.
Instead of writing the validation rules ourselves, Zod provides a powerful and easy-to-use schema validation for the above scenario.
We’ll start with creating the project folder.
Navigate to any project directory of choice on your terminal, then run the following:
mkdir zod-app
This creates a folder called zod-app
in your project directory.
Navigate to the new folder with the following:
cd zod-app
We will initialize a new Node project with the following:
npm init -y
This will create a package.json
file in the zod-app
folder.
Dependencies
To get started, we will install the following dependencies:
npm install typescript --save-dev
npm install zod
npm install ts-node --save-dev
With the above, we have installed TypeScript and Zod in the project.
Run the following:
npx tsc --init
This initializes a tsconfig.json
file: This file is used to configure TypeScript settings.
Getting Started With Zod
We can finally get our hands dirty with some code. To get started, we will implement the scenario (the user registration form). Navigate to the project folder (zod-app
) with any IDE of your choice. Then create an index.ts
folder and add the following code:
import { number, object, string } from 'zod';
const UserSchema = object({
username: string({ required_error: 'Username is required' }).min(3),
email: string({ required_error: 'Email is required' }).email(),
password: string({ required_error: 'Password is required' }).min(8),
age: number().min(18).optional(),
});
let invalidUser = {
username: 'Joe',
password: 'password',
age: 17,
}
const invalidResult = UserSchema.safeParse(invalidUser);
console.log(invalidResult.success); // false
console.log(invalidResult.data) // undefined
console.log(invalidResult.error?.errors); // { errors: [ { message: 'Email is required', path: [ 'email' ] } ] }
let validUser = {
username: 'Joe',
email: 'joe@doe.com',
password: 'password',
age: 18,
}
const validResult = UserSchema.safeParse(validUser);
console.log(validResult.success); // true
console.log(validResult.data) // { username: 'Joe', ... }
console.log(validResult.error); // undefined
The UserSchema
is a Zod object schema that defines the properties of the object. From the code, we see that the username
email
and password
fields are required fields of the string
type. The .email()
method extends the string to restrict incoming strings to be valid email types. The .min()
method extends the string to validate strings that are above the minimum length that was set. This shows how Zod provides a powerful and easy way of performing schema validation.
Applying Zod to an Express Application
We will build a middleware that uses Zod as the validator for clients’ incoming data. As shown above, Zod provides a clean and easy way to provide powerful schema validation. Let’s start by installing Express into the project:
npm install express
npm install @types/express --save-dev
Create a file named app.ts
and write the following code:
import express from 'express';
const app = express();
app.use(express.json());
app.post('/users', (req, res) => {
const user = req.body;
console.log(user);
res.status(201).json(user);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
This creates a simple server that has a /users
endpoint. The endpoint prints the request body and returns a 201
status code. Now, we do not want to send a success status code when the data coming from the user is invalid. So we need to protect the endpoint with a middleware function.
Create a file called middleware.ts
and paste in the following code:
import zod from 'zod';
export const ZodMiddleware = (schema: zod.AnyZodObject) => {
return async (req: any, res: any, next: any) => {
let result = schema.safeParse(req.body);
if (!result.success) {
res.status(400).json({ error: result.error.errors });
return;
}
req.body = result.data;
next();
}
}
The above code creates a middleware function. It takes any Zod type (ZodAny
) as a parameter and validates the incoming request body against the schema. If the result is not successful, it returns a 400
status code. If the data is valid, it calls the next
function, which passes the request to the main request handler.
We will create a schema.ts
file to write the code for the example schema:
import { number, object, string } from 'zod';
export const UserSchema = object({
username: string({ required_error: 'Username is required' }).min(3),
email: string({ required_error: 'Email is required' }).email(),
password: string({ required_error: 'Password is required' }).min(8),
age: number().min(18).optional(),
});
This is the same schema as the one in the index.ts
file but we have separated it for a cleaner result.
We will adjust the app.ts
code to use the middleware:
import express from 'express';
import { UserSchema } from './schema';
import { ZodMiddleware } from './middleware';
const app = express();
app.use(express.json());
app.post('/users', ZodMiddleware(UserSchema), (req, res) => {
const user = req.body;
console.log(user);
res.status(201).json(user);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
We have now set up the /users
endpoint to use the ZodMiddleware
function, which will validate the request body against the UserSchema
. Now any invalid request will be blocked by the middleware. To test the API, you can use an API-testing client like Postman or the VSCode ThunderClient extension.
Conclusion
Using Zod significantly enhances and simplifies the schema validation process. Zod is a flexible, powerful and efficient library that can be used in multiple scenarios. It is also TypeScript-friendly which aids the development process.
This article has provided valuable insights into simplifying input validation in Express applications using Zod. If there are other topics you'd like to explore or if you have any questions, feel free to share them in the comments below. We’d love to hear your suggestions and know what you’d like to learn about next!
References
Zod Library Documentation: https://zod.dev/
Express Middlewares: https://expressjs.com/en/guide/using-middleware.html
Subscribe to my newsletter
Read articles from Kcee directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Kcee
Kcee
I am a software developer with 3+ years of experience using Vue, Django and other fun frameworks. I love to experiment and test out interesting features. Writing is a hobby I enjoy and want to explore more of. I am also interested in visual arts.