Search code examples
node.jsexpressexpress-validator

Express Validator: Destruction of nested object is painful - is there a better way?


I'm using nodejs/mongoDB/mongoose/express-validator and I am trying to output the error-messages via validationResult(req). It's my understanding that this returns a nested object which I then have to destruct in order to access the messages.

Edit: It's express-validator version 4.x (Sept 2017) which is why I cannot find better documentation/tutorials ... all the current available tuts refer to methods which won't work anymore.

app.post("/register",
check("username").isEmail().withMessage("Ungültige E-Mailadresse"),
check("password").isLength({ min: 4 }).withMessage("Ungültiges Passwort"),
check("password").equals("password-repeat").withMessage("Passwort stimmt nicht überein"),
function (req, res) {
    var errors = validationResult(req).mapped();
    if (errors) {
        try {
            var { username: { msg: username_error } } = errors;
        } catch(e) {
            console.log("Undefined shit u so");
        }
        try {
            var { password: { msg: password_error } } = errors;
        } catch(e) {
            console.log("Undefined shit u so");
        }
        var messages = [username_error, password_error];
        res.render("register", {
            title: "Register",
            errors: messages
        })
    } else {
        res.redirect("/register");
    }
})

The above code works but is a pain ... If I don't try/catch every possible TypeError (cannot destruct undefined or null), nodejs crashes.

Is there a better way to create an array with only existing error-messages?

Thanks! Philip


Solution

  • First of all, if you want to check for errors existence then you have to use .isEmpty() method:

    var errors = validationResult(req);
    
    if ( errors.isEmpty() ) {
        res.redirect("/register");
    } else {
        // send validation messages
    }
    

    From your code above, it seems that you are looking for changing the errors object into array of messages to show them in the view. another way consists of sending the mapped object to your view and then check for errors existence based on field names, here is an example:

    route handler:

    var errors = validationResult(req);
    
    if ( errors.isEmpty() ) {
        res.redirect("/register");
    } else {
        res.render("register", {
            title: "Register",
            errors: errors.mapped();
        });
    }
    

    View (example using ejs view engine)

    <div>
        <input name="username" type="text">
        <!-- check if there is any errors related to username input -->
        <% if ( errors.hasOwnProperty('username') ) { %>
            <span class="error"> <%= errors.username.msg %> </span>
        <% } %>
    </div>
    
    <div>
        <input name="password" type="password">
        <!-- check if there is any errors related to username input -->
        <% if ( errors.hasOwnProperty('password') ) { %>
            <span class="error"> <%= errors.password.msg %> </span>
        <% } %>
    </div>
    

    Remember that the mapped() method return an object where the keys are the field names, and the values are the validation errors

    EDIT If you want to render errors as array then use .array() method:

    res.render("register", {
        title: "Register",
        errors: errors.array({ onlyFirstError: true });
    });
    

    You can then iterate over the array of errors from the view and show all errors in a list, for example:

    <ul>
        <% for ( var i = 0; i < errors.length; i++ ) { %>
            <li>field: <%= errors[i].param %>, Error message: <%= errors[i].msg %></li>
        <% } %>
    </ul>