node.jsajaxexpressexpress-validator

Ajax 400 error after Express Validator runs server-side validation


I am trying to fix a form handler that always throws a 400 error after middleware validation.

The middleware validation goes like this:

const contactValidate = [
  check('name')
    .exists()
    .trim()
    .escape()
    .not()
    .isEmpty()
    .withMessage('Name can not be empty!')
    .bail()
    .isLength({min: 2})
    .withMessage('Minimum 2 characters required!')
    .bail()
    .isAlpha('en-US', {ignore: ' '})
    .withMessage('Name can only have letters and spaces')
    .bail(),
  check('phone')
    .trim()
    .escape()
    .not()
    .isEmpty()
    .withMessage('Phone can not be empty!')
    .bail()
    .isMobilePhone()
    .withMessage('Must be a phone number.')
    .bail(),
  check('email')
    .trim()
    .normalizeEmail()
    .not()
    .isEmpty()
    .withMessage('Invalid email address!')
    .bail(),
  check('note')
    .exists()
    .trim()
    .escape()
    .not()
    .isEmpty()
    .withMessage('Message can not be empty!')
    .bail(),
  check('botCheck')
    .isIn(["on"])
    .withMessage("Bot Check Failed")
    .bail(),
  function (req, res, next) {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    next();
  },
];

It is supposed to hand off errors to this ajax call:

const form = document.getElementById('contact-form');

form.addEventListener('submit', (e) => {
  e.preventDefault();

  const formData = new FormData(form);

  fetch('/contact/', {
      method: 'POST',
      body: formData,
      mode: 'cors',
      contentType: "application/json; charset=utf-8"
    })
    .then((response) => {
      response.json();
    })
    .then((response) => {
      // handle errors
      if (response.errors) {
        response.errors.forEach(({ msg }) => {
          document.getElementById('status').innerHTML += `<p class="note note-danger">${msg}</p>`
        });
        return;
      }
      // If mail was sent successfully, reset all elements with attribute 'name'
      const values = document.querySelectorAll('[name]');
      values.forEach( value => {
        value.textContent = '';
      });

      document.getElementById('status').innerHTML = `<p class="note note-success">${response.msg}</p>`;
    })
    .catch((err) => {
      document.getElementById('status').innerHTML += `<p class="note note-danger">${err}</p>`
    })
    .finally(()=> {
      setTimeout(()=> {
        document.getElementById('status').innerHTML = '';
      }, 2000)
    })
});

The start of the form has the right HTMl elements: <form id="contact-form" class="needs-validation" novalidate enctype="multipart/form-data">

I can not get it past the 400 error that says "TypeError: Cannot read properties of undefined (reading 'errors')". I am not sure if it's my server side validation in express-validator or the AJAX call that is incorrect.


Solution

  • Ok, I realized after looking back that it was the middleware that step 1 of the problem and then the Ajax handle I was using was formatted incorrectly to handle the response.

    The correct Middleware should be:

    
    module.exports = [
    body("name")
      .trim()
      .notEmpty().withMessage('Name is required.')
      .isAlpha('en-US', {ignore: '\s'}).withMessage('Name can only have letters.')
      .bail(),
    
    body("email")
      .trim()
      .notEmpty().withMessage('Email is required.')
      .isEmail().withMessage('Invalid email address.')
      .bail(),
    
    body("phone")
      .trim()
      .notEmpty().withMessage('Phone number is required.')
      .isMobilePhone('en-US').withMessage("Please make sure it is a valid phone number")
      .bail(),
    
    body("note")
      .trim()
      .notEmpty().withMessage('Message is required.')
      .bail(),
    
    body("botCheck").isIn(["on"]).withMessage("Bot Check Failed"),
    
    
      function (req, res, next) {
        const errors = validationResult(req);
        console.log("Validation Result:", errors); 
        if (!errors.isEmpty()) {
          return res.status(400).json({ errors: errors.array() });
           
        }
        next();
      },
    ];
    

    Then the AJAX to handle that response should be:

    const form = document.getElementById('contact-form');
    
    form.addEventListener('submit', (e) => {
      e.preventDefault();
    
      const formData = new FormData(form);
      for (var pair of formData.entries()) {
          console.log(pair[0]+ ', ' + pair[1]); 
      }
      console.log(formData);  // Log form data
    
      fetch('/contact', {
        method: 'POST',
        body: formData,
        mode: 'cors'
      })
      .then((response) => {
        return response.json();  // Otherwise, proceed as usual
      })
      .then((response) => {
        // handle errors
        if (response.errors) {
          response.errors.forEach(({ msg }) => {
            document.getElementById('status').innerHTML += `<p class="note note-danger">${msg}</p>`;
          });
          return;
        }
        // If mail was sent successfully, reset all elements with attribute 'name'
        const values = document.querySelectorAll('[name]');
        values.forEach(value => {
          value.textContent = '';
        });
        document.getElementById('status').innerHTML = `<p class="note note-success">${response.msg}</p>`;
        // Clear the form
        form.reset();
      })
      .catch((err) => {
        document.getElementById('status').innerHTML += `<p class="note note-danger">${err}</p>`;
      })
      .finally(() => {
        setTimeout(() => {
          document.getElementById('status').innerHTML = '';
        }, 2000);
      });
    });
    

    I realize now that the 400 error was due to malformed express-validator code and I just needed to process the formData in the Ajax and it worked perfectly.

    Thanks for the help.