Introduction
When you build an API, users send data to your backend — things like email addresses, usernames, and passwords. But what happens if that data is invalid or missing?
That’s where validation comes in. Validation means checking that the incoming data is correct before using it.
In this guide, you’ll learn how to use express-validator in a TypeScript + Express project to validate and sanitize user input. We’ll go step by step, so even if you’re new to TypeScript or Express, you’ll be able to follow along easily.
Why You Need express-validator
express-validator
is a middleware that makes input validation simple and readable.
Instead of manually checking if req.body.email looks like an email or if the password is long enough, you just describe the rules, and express-validator does the checking for you.
For example:
body("email").isEmail().withMessage("Please enter a valid email");
This line says: “The email field must be a valid email.”
If it’s not, express-validator automatically collects the error for you.
Project Structure
Before diving into the code, here’s what your project structure will look like when we’re done:
express-ts-validator/
│
├── src/
│ ├── server.ts
│ ├── routes/
│ │ └── user.ts
│ ├── middlewares/
│ │ └── validate.ts
│
├── tsconfig.json
├── package.json
What each file does:
server.ts – sets up Express, middlewares,routes and starts the Express server.
routes/user.ts – holds all user-related routes (like registration).
middlewares/validate.ts – reusable function to handle validation errors.
tsconfig.json – TypeScript configuration file.
Setting Up the Project
Step 1: Initialize the Project
Create a new folder and initialize npm.
mkdir express-ts-validator
cd express-ts-validator
npm init -y
Step 2: Install Dependencies
npm install express express-validator
npm install --save-dev nodemon typescript ts-node @types/express @types/node
Step 3: Configure TypeScript
Initialize TypeScript configuration:
npx tsc --init
Update tsconfig.json
to include:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"strict": true
}
}
This tells TypeScript how to compile your code.
Step 4: Configure scripts
Update package.json with:
"scripts": {
"start": "node dist/server.js",
"build": "tsc p .",
"dev": "nodemon src/server.ts"
}
Setting Up Express with TypeScript
Create a basic Express server in src/server.ts
:
import express, { Request, Response } from "express";
const PORT = 3000;
const app = express();
// Enable URL-encoded form data parsing
app.use(express.urlencoded({ extended: true }));
// Middleware to parse JSON
app.use(express.json());
app.get("/", (req: Request, res: Response) => {
res.json({ message: "Hello from Express with TypeScript" });
});
// Start server
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Run your app:
npm run dev
Then visit:
http://localhost:3000
You should see:
{ "message": "Hello, Express with TypeScript!" }
Validating Input with express-validator
Let’s make a POST endpoint for user registration.
We’ll check:
Email is valid.
Password is at least 6 characters.
Username is not empty and has at least 3 characters.
Create a new folder
src/routes/
and inside it a fileuser.ts
.
src/routes/user.ts
import { Router, Request, Response } from "express";
import { body, validationResult } from "express-validator";
const router = Router();
router.post(
"/register",
[
body("email").isEmail().withMessage("Please enter a valid email"),
body("password")
.isLength({ min: 6 })
.withMessage("Password must be at least 6 characters long"),
body("username")
.notEmpty()
.isLength({ min: 3 })
.withMessage("Username must be at least 3 characters long")
],
(req: Request, res: Response) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, username } = req.body;
res.json({ message: "User registered successfully", data: { email, username } });
}
);
export default router;
Then, connect the route to your app in src/server.ts:
import express, { Request, Response } from "express";
import userRoutes from "./routes/user";
const PORT = 3000;
const app = express();
// Enable URL-encoded form data parsing
app.use(express.urlencoded({ extended: true }));
// Middleware to parse JSON
app.use(express.json());
// Connecting user route to your app
app.use("/api/users", userRoutes);
app.get("/", (req: Request, res: Response) => {
res.json({ message: "Hello from Express with TypeScript" });
});
// Start server
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Now if you send a POST request,using a tool like Postman, to:
http://localhost:3000/api/users/register
with invalid data, you’ll get clear error messages from express-validator.
Handling Validation Errors
Right now, every route has its own validationResult
logic.
If your project grows, that can get repetitive.
Let’s move that logic into its own file so we can reuse it anywhere.
Creating a Reusable Validation Middleware
Create a new folder src/middlewares/
and add a file called validate.ts
.
src/middlewares/validate.ts
import { Request, Response, NextFunction } from "express";
import { validationResult } from "express-validator";
export function validate(req: Request, res: Response, next: NextFunction) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
}
Now, you can import and use it anywhere like this:
import { Router,Request,Response } from "express";
import { body } from "express-validator";
import { validate } from "./../middlewares/validate"; // Importing the validate middleware
const router = Router();
router.post(
"/register",
[
body("email").isEmail().withMessage("Invalid email"),
body("password").isLength({ min: 6 }).withMessage("Password too short"),
body("username").notEmpty().withMessage("Username is required")
],
validate,// Registering the middleware in the register route
(req: Request, res: Response) => {
const { email, username } = req.body;
res.json({ message: "User registered successfully", data: { email, username } });
}
);
This approach keeps your routes clean and easy to maintain.
Wrapping Up
You’ve just built a clean, type-safe Express API that validates user input with express-validator.
Here’s what you learned:
How to set up an Express + TypeScript project.
How to validate user input using express-validator.
How to handle validation errors using a reusable middleware.
From here, you can improve your project by:
Adding sanitization (e.g., trimming whitespace).
Writing custom validators (like checking if an email already exists).
Organizing routes and validators into separate files for bigger apps.
By learning this pattern early, you’re building a solid foundation for writing secure and maintainable backend applications.
The code for this blog is here.
Top comments (0)