Validating & Sanitizing Input
Never trust data from the outside world. Reject bad input cleanly — before it reaches your database — with a validation library.
What you will learn
- Explain why server-side validation is non-negotiable
- Validate request bodies with a validation library
- Return clear 400 errors listing what was wrong
Why validate on the server at all?
Your React form might check that a field is filled in — but a determined user (or a buggy script, or an attacker) can send a request straight to your API with tools like Postman, skipping the front-end entirely. The browser’s checks never run. So your server is the only place you fully control, and it must assume every incoming request could be wrong, missing fields, or even malicious.
Validation means checking that incoming data is shaped the way you expect (the right fields, the right types, sensible lengths) and rejecting it early if not. Sanitization is the cleanup cousin — trimming stray spaces, stripping dangerous characters — so only tidy data reaches your database.
- A request arrives at a route that expects, say, a
textfield. - A validation step inspects the body before your main logic runs.
- If anything is wrong, you stop immediately and reply
400(Bad Request) with a message naming the problem. - Only fully valid requests reach the code that saves to the database.
The manual way (and why it does not scale)
You already saw a tiny hand-written check in the error-handling lesson: if (!req.body.text) return res.status(400).... That works for one rule, but real endpoints need many — required, correct type, minimum and maximum length, valid email format. Writing all that by hand for every route quickly becomes a tangle. A validation library lets you declare the rules once, clearly.
Validation with express-validator
express-validator is a popular library that plugs in as middleware. You list the rules for each field, and it collects any failures for you to inspect. Install it first:
npm install express-validatorNote: Output:
added a few packages in 1s
This adds express-validator to your project so you can require its body and validationResult helpers.
Now attach an array of rules to the route, then check the results at the top of the handler:
const { body, validationResult } = require("express-validator");
app.post(
"/tasks",
[
body("text").trim().notEmpty().withMessage("text is required")
.isLength({ max: 200 }).withMessage("text must be 200 chars or fewer")
],
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// safe to use req.body.text here — it passed every rule
res.status(201).json({ text: req.body.text });
}
);Line by line: body("text") targets the text field of the request body. .trim() sanitizes it by removing surrounding spaces. .notEmpty() requires a value, and .isLength({ max: 200 }) caps its length — each with a friendly .withMessage(...). Those rules run as middleware. Inside the handler, validationResult(req) gathers any failures; if the list is not empty, we reply 400 with the details. If it is empty, every rule passed and the data is safe to save.
Note: Output (POST /tasks with an empty body):
{"errors":[{"type":"field","msg":"text is required","path":"text","location":"body"}]}
Output (POST /tasks with {"text":"Buy milk"}):
{"text":"Buy milk"}
The bad request never touches your database — it bounces with a clear, structured 400 telling the front-end exactly which field failed and why. The good request sails through.
Tip: Many teams instead use Zod or Joi, which describe the whole expected shape as a single schema object (for example z.object({ text: z.string().min(1).max(200) })). The idea is identical — declare the rules, reject anything that does not match — so once you understand one, the others read the same.
Watch out: Validation and authentication are different jobs. Validation asks "is this data well-formed?"; authentication (next lesson) asks "who is sending it, and are they allowed?". A real API needs both.
Q. Why is server-side validation essential even when your React form already validates?
✍️ Practice
- Add express-validator rules to a POST route requiring a non-empty
textunder 200 characters, and return a structured 400 on failure. - Add a route that validates an
emailfield with.isEmail()and test it with a bad address.
🏠 Homework
- Validate a "signup" body — require a
name, a validemail, and apasswordof at least 8 characters — and return all failing rules at once.