Introduction
Express.js is a popular web framework for building Node.js applications, and validation is a crucial aspect of handling incoming data securely. In this article, we'll explore how to create a generic validation middleware using Zod, a TypeScript-first schema declaration and validation library. This middleware can be reused across different routes to ensure that incoming data adheres to predefined schemas.
Prerequisites
Before diving into the implementation, make sure you have the following prerequisites installed:
- Node.js and npm
- TypeScript
- Express.js
- Zod
Install the necessary packages by running:
npm install express body-parser zod
Directory Structure
To keep our code organized, we'll structure our project as follows:
project-root
|-- src
| |-- routes
| | |-- userRoutes.ts
| |
| |-- middleware
| | |-- validationMiddleware.ts
| |
| |-- schemas
| | |-- userSchemas.ts
| |
| |-- index.ts
|
|-- package.json
|-- tsconfig.json
|-- ...
Implementing Generic Validation Middleware
Step 1: Set Up Express App
In your index.ts
file, set up an Express app and configure middleware:
// src/index.ts
import express from 'express';
import bodyParser from 'body-parser';
import userRouter from './routes/userRoutes';
const app = express();
const PORT = 3000;
app.use(bodyParser.json());
app.use('/api/user', userRouter);
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Step 2: Create Zod Schemas
Define Zod schemas for different sections in your userSchemas.ts
file:
// src/schemas/userSchemas.ts
import { z } from 'zod';
export const userRegistrationSchema = z.object({
username: z.string(),
email: z.string().email(),
password: z.string().min(8),
});
export const userLoginSchema = z.object({
username: z.string(),
password: z.string().min(8),
});
Step 3: Build Generic Validation Middleware
Create a generic validation middleware in validationMiddleware.ts
:
// src/middleware/validationMiddleware.ts
import { Request, Response, NextFunction } from 'express';
import { z, ZodError } from 'zod';
import { StatusCodes } from 'http-status-codes';
export function validateData(schema: z.ZodObject<any, any>) {
return (req: Request, res: Response, next: NextFunction) => {
try {
schema.parse(req.body);
next();
} catch (error) {
if (error instanceof ZodError) {
const errorMessages = error.errors.map((issue: any) => ({
message: `${issue.path.join('.')} is ${issue.message}`,
}))
res.status(StatusCodes.BAD_REQUEST).json({ error: 'Invalid data', details: errorMessages });
} else {
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: 'Internal Server Error' });
}
}
};
}
Step 4: Utilize the Generic Validation Middleware in Routes
In your userRoutes.ts
, use the generic middleware for different routes:
// src/routes/userRoutes.ts
import express from 'express';
import { validateData } from '../middleware/validationMiddleware';
import { userRegistrationSchema, userLoginSchema } from '../schemas/userSchemas';
const userRouter = express.Router();
import { registerUser, loginUser } from './userController';
userRouter.post('/register', validateData(userRegistrationSchema), registerUser);
userRouter.post('/login', validateData(userLoginSchema), loginUser);
export default userRouter;
Step 5: Implement User Controller Logic
Create a user controller in userController.ts
to handle user registration and login:
// src/routes/userController.ts
import { Request, Response } from 'express';
export const registerUser = (req: Request, res: Response) => {
// Handle user registration logic using validated data from req.body
res.json({ message: 'User registered successfully', data: req.body });
};
export const loginUser = (req: Request, res: Response) => {
// Handle user login logic using validated data from req.body
res.json({ message: 'User logged in successfully', data: req.body });
};
Conclusion
Congratulations! You've successfully implemented a generic validation middleware using Zod in your Express.js application. This approach allows you to create reusable validation logic for different sections of your application, promoting maintainability and scalability.
Feel free to customize the schemas and middleware according to your specific validation requirements. This structure not only enhances the readability of your code but also facilitates easier testing and code maintenance.
Top comments (9)
amazing and simple thx
This is great, right to the point.
Thanks!!!!
Thank you Vignesh
Very nice!
Thanks
Really useful, thanks!
Thank you @adrielfsantos
How can i validate the params too? Great article, thanks!
You can try something like this for a validation.middleware.ts: