Express Server-Side Validation

Registered members can download the FREE Get Started App. This is the project I used to compose articles about setting up VS Code and developing Node with Express and the Embedded JavaScript (EJS) view engine.

There are server-side Express libraries for validation like 'express-validator', 'zod', and 'joi'. I mainly want to detect client-side validation failures, so I try to validate the same requirements on the sever. Because I log errors on the server, I can throw errors and get notified.

This series will focus on posting form data from the client to the server. Validation should notify the user of invalid input as soon as possible. Password requirements should be listed and indicate if the requirement has been satisfied.

I will use the Get Started App's contact us form for this series. I added a checkbox and a radio group for demonstration. The form is in a Bootstrap modal using Bootstrap classes.

Contact Us Modal

I use the same Regular Expressions to validate on the server as I do for the client. The Get Started App implements an InputValidator module. Add an input-validator.mjs file in the utilities folder.

input-validator.mjs
const InputValidator = {};

InputValidator.emailAddress = (emailAddress) => {
    const result = { valid: false, error: '' };
    const emailRegExp = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
    if (emailRegExp.test(emailAddress)) result.valid = true;
    else result.error = 'Invalid email address format.';
    return result;    
}

InputValidator.containsHTML = (text) => {
    const result = { valid: false, error: '' };
    const htmlTagRegExp = /<[^>]+>/;
    const htmlEntityRegExp = /&[a-z0-9#]+;/i;
    if (htmlTagRegExp.test(text) || htmlEntityRegExp.test(text))
       result.error = 'Contains basic HTML tags or entities.';
    else result.valid = true;
    return result;
}

export default InputValidator;

The Get Started App implements routers and controllers. The root-router.mjs imports the RootController and routes the post to the PostContactUs function. The root-controller.mjs imports the InputValidator.

I added a checkbox and radio group for demonstration. If the checkbox is not checked, the checkbox's name key is undefined. If the checkbox is checked, the key's value is equal to the checkbox's value attribute. You can evaluate the expected value for a Boolean type. If no radio input is selected for the radio group, the name key is undefined, otherwise the key is equal to the selected input value attribute.

The verifyhuman field is the Human Verification input with an expected value of 2. The InputValidator.emailAddress function validates a proper email format. The InputValidator.containsHTML function detects harmful html markup. I validate the expected min and max lengths for the subject and message fields.

root-controller.mjs
import InputValidator from '../utilities/input-validator.mjs';

RootController.PostContactUs = async (req, res, next) => {
  try {
    const { email, subject, message, urgent, scale, verifyhuman } = req.body;
    const isUrgent = urgent === 'true';
    let scaleInt = 0;
    if (typeof scale !== 'undefined') {
      scaleInt = parseInt(scale);
      if (Number.isNaN(scaleInt)) throw new Error("Scale is not a number.");
    }

    if (parseInt(verifyhuman) != 2) throw new Error("Human verification failed.");
    let validatorResult = InputValidator.emailAddress(email);
    if (!validatorResult.valid) throw new Error(validatorResult.error);
    if (subject.length < 8 || subject.length > 100) throw new Error("Subject length validation failed.");
    validatorResult = InputValidator.containsHTML(subject);
    if (!validatorResult.valid) throw new Error(validatorResult.error);
    if (message.length < 20 || message.length > 500) throw new Error("Message length validation failed.");
    validatorResult = InputValidator.containsHTML(message);
    if (!validatorResult.valid) throw new Error(validatorResult.error);

    // Configure contact us mailOptions
    ...

    const senderResult = await EmailSender.send(mailOptions);
    if (senderResult != null && senderResult.success) {
      console.log("Email sent: ", senderResult.info.response);
      return res.status(201).redirect('/about?statusalert=Email sent. We will respond ASAP.');
    } else {
      let error = "Error: result is null.";
      if (senderResult != null) { error = "Error: " + senderResult.error; }
      console.error(error);
      return res.status(201).redirect('/about?statusalert=Error: There was a problem sending the email.');
    }
  } catch (err) {
    next(err);
  }
}
Created: 2/16/26