Server-side data validation in ExpressJS

Server-side data validation in ExpressJS

ExpressJS can rely on a specific NPM module to perform server-side data validation.

ExpressJS can rely on a specific NPM module to perform server-side data validation.

express-validator is a middleware that validates data before the callback function that handles a route is executed. For data validation and filtering features it is entirely based on the validator.js module from which it inherits the main methods.

In a typical form POST request, this module defines its rules as an array of middlewares and then makes the results available by adding an array of objects to the request object.

'use strict';

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

app.post('/contact',
[
  body('email').isEmail(),
  body('subject').isLength({ min: 5 }),
  body('message').isLength({ min: 100 }),
  
], (req, res) => {
    const validationErrors = validationResult(req);
    
    if (!validationErrors.isEmpty()) {
        return res.status(400).render('contact', { errors: validationErrors.array() } );
      }
      
      //...
});

body('fieldname') checks the parameters of the request.body object that is populated when submitting the form as a POST request. fieldname matches the name of the name attribute in an HTML form and therefore the name of the POST request parameter.

We first check if there are any validation errors using the isEmpty() method. So the output obtained by transforming the results of the validationResult () function into an array is the following.

[
    {
      location: 'body',
      msg: 'Invalid value',
      param: 'email'
    },
    {
      location: 'body',
      msg: 'Invalid value',
      param: 'subject'
    },
    {
      location: 'body',
      msg: 'Invalid value',
      param: 'message'
    }
]

Error messages (msg property) can be customized via the withMessage() method.

'use strict';

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

app.post('/contact',
[
  body('email').isEmail().withMessage('Invalid e-mail.'),
  body('subject').isLength({ min: 5 }).withMessage('Required field.'),
  body('message').isLength({ min: 100 }).withMessage('Required field.'),
  
], (req, res) => {
    const validationErrors = validationResult(req);
    
    if (!validationErrors.isEmpty()) {
        return res.status(400).render('contact', { errors: validationErrors.array() } );
      }
      
      //...
});

This module also supports custom validation functions via the custom() method. This method must raise an error or reject a Promise when the verification fails.

'use strict';

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

app.post('/contact',
[
  body('email').isEmail(),
  body('subject').isLength({ min: 5 }),
  body('message').isLength({ min: 100 }).custom(value => {
     if(/<|>/g.test(value)) {
         throw new Error('Invalid characters found.');
     }
     return true; // Nessun errore
  }),
  
], (req, res) => {
    const validationErrors = validationResult(req);
    
    if (!validationErrors.isEmpty()) {
        return res.status(400).render('contact', { errors: validationErrors.array() } );
      }
      
      //...
});

How can we display error messages in the views? There are basically two approaches. The first shows the list of errors at the beginning or at the end of the form. For example, using EJS as a template engine:


<% if(errors.length > 0) { %>
    <% errors.forEach(err => { %>
        <div class="alert alert-danger"><%= err.msg %></div>
    <% }); %>
<% } %>

<form action="/contact" method="post" id="contact-form">

</form>

The second approach shows each error next to its respective field.

<% const hasErrors = errors.length > 0; %>

<form action="/contact" method="post" id="contact-form">
    <div class="form-group">
        <label for="email">E-mail</label>
        <input type="email" name="email" id="email" class="form-control">
        
        <% if(hasErrors) {
           const emailErr = errors.find(e => e.param === 'email');
           if(emailErr) {
        %>
        
        <div class="alert alert-danger"><%= emailErr.msg %></div>
        
        <% }
        
          } %>
    </div>
    <div class="form-group">
        <label for="subject">Subject</label>
        <input type="text" name="subject" id="subject" class="form-control">
        
        <% if(hasErrors) {
           const subjErr = errors.find(e => e.param === 'subject');
           if(subjErr) {
        %>
        
        <div class="alert alert-danger"><%= subjErr.msg %></div>
        
        <% }
        
          } %>
    </div>
    
    <div class="form-group">
        <label for="message">Message</label>
        <textarea name="message" id="message" class="form-control">
        
        <% if(hasErrors) {
           const msgErr = errors.find(e => e.param === 'message');
           if(msgErr) {
        %>
        
        <div class="alert alert-danger"><%= msgErr.msg %></div>
        
        <% }
        
          } %>
    </div>

        <p><input type="submit" value="Send" class="btn btn-primary"></p>
</form>

Ultimately this module proves to be extremely useful for automating and simplifying the data validation process in an application in ExpressJS.