Node.js Input Validation

Input validation checks every piece of data before your app uses it. Visitors make typing mistakes, and attackers send harmful data on purpose. A good check blocks both problems at the door.

The Security Guard Idea

Picture a guard at a building entrance. The guard checks each visitor's badge before opening the door. Validation plays that role for your routes.

 Request --> [ Validation Guard ] --> Route logic --> Database
                    |
                    +--> Invalid? Reject with a clear message

What to Check

  • Presence: the field exists and is not empty.
  • Type: a number is a number, and text is text.
  • Length: the value fits a minimum and maximum size.
  • Format: emails, phone numbers, and dates follow a pattern.
  • Range: an age sits between sensible limits.

Manual Checks

Small apps can validate with plain JavaScript.

app.post("/register", (req, res) => {
  const { name, age } = req.body;
  const errors = [];

  if (typeof name !== "string" || name.trim().length < 2) {
    errors.push("Name needs at least 2 letters.");
  }
  if (!Number.isInteger(age) || age < 13 || age > 120) {
    errors.push("Age must be a whole number from 13 to 120.");
  }

  if (errors.length > 0) {
    return res.status(400).json({ errors });
  }
  res.status(201).json({ message: "Registered" });
});

Manual code grows long in bigger projects. A validation library keeps rules tidy.

Validate with Joi

Joi describes the rules in one schema object. The schema works like a blueprint for valid data.

npm install joi
const Joi = require("joi");

const userSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  age: Joi.number().integer().min(13).max(120),
  role: Joi.string().valid("student", "teacher").default("student")
});

app.post("/users", (req, res) => {
  const { error, value } = userSchema.validate(req.body, { abortEarly: false });

  if (error) {
    const messages = error.details.map((d) => d.message);
    return res.status(400).json({ errors: messages });
  }
  res.status(201).json(value);
});

The option abortEarly: false reports every problem at once, so visitors fix everything in one try.

Reusable Validation Middleware

Middleware lets you apply one schema to any route with a single line.

function validate(schema) {
  return (req, res, next) => {
    const { error, value } = schema.validate(req.body, { abortEarly: false });
    if (error) {
      return res.status(400).json({
        errors: error.details.map((d) => d.message)
      });
    }
    req.body = value;
    next();
  };
}

app.post("/users", validate(userSchema), (req, res) => {
  res.status(201).json(req.body);
});
Request --> validate(userSchema) --> route handler
                  |
                  +--> 400 response with error list

Validate with express-validator

The express-validator library places rules directly inside route definitions.

const { body, validationResult } = require("express-validator");

app.post(
  "/signup",
  body("email").isEmail().normalizeEmail(),
  body("password").isLength({ min: 8 }),
  (req, res) => {
    const result = validationResult(req);
    if (!result.isEmpty()) {
      return res.status(400).json({ errors: result.array() });
    }
    res.send("OK");
  }
);

Validate More Than the Body

  • req.params holds values from the path, such as an item id.
  • req.query holds search words, such as page numbers.
  • req.headers holds details sent with the request.
app.get("/items/:id", (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id) || id < 1) {
    return res.status(400).send("Item id must be a positive whole number.");
  }
  res.send("Item " + id);
});

Clean the Data

Sanitizing changes input into a safe, tidy form. Trim spaces, convert emails to lowercase, and remove unwanted fields. Joi removes unknown keys with the option stripUnknown: true.

Client and Server Checks

Browser checks give quick feedback. Attackers skip the browser and call your server directly. Always validate on the server.

Practice Tasks

  • Write a Joi schema for a product with a name, price, and stock count.
  • Reject negative prices with a helpful message.
  • Validate a page number in the query string and default it to 1.