Search code examples
javascriptnode.jstypescriptexpressexpress-validator

Auto-generated error messages for Express-Validator


I want to create an Express API and validate my request input with express-validator. Currently this is my validation middleware

protected validate = async (request: Request, response: Response, next: NextFunction): Promise<void> => {
    const validationErrors: Result<ValidationError> = validationResult(request);

    if (!validationErrors.isEmpty()) {
        response.status(422).json({ errors: validationErrors.array() });
    } else {
        next();
    }
};

and my basic validation setup looks like

public persist = [
    body('username')
        .isString()
        .withMessage('username must be of type string.')
        .isLength({ min: 1 })
        .withMessage('username must be at least one character long.')
        .exists()
        .withMessage('username is required.'),
    body('password')
        .isString()
        .withMessage('password must be of type string.')
        .isLength({ min: 1 })
        .withMessage('password must be at least one character long.')
        .exists()
        .withMessage('password is required.'),
    this.validate,
];

When I call POST /users to create a new user I get a detailed error response on invalid inputs. When I remove all the custom error messages I get this response

[
    {
        "msg": "Invalid value",
        "param": "username",
        "location": "body"
    },
    {
        "msg": "Invalid value",
        "param": "password",
        "location": "body"
    }
]

Is there a way to get auto-generated error messages or do I really have to write these error messages on my own?


Solution

  • Basically, the validator is trying to tell you where it thinks it has found an error and since it does not have the custom error string(s), it falls back on the (rather ugly) "element/location" scheme.

    Approach 1

    The most reasonable way would be to have a custom error message for each field. Seeing that the error messages are rather repetitive, it would make sense to move them out of the validator definition, and maybe create a very simple "getter" function that would do some basic templating. Then, your code becomes a fair bit nicer:

    const MSG_TYPE_STR = '{{field}} must be of type string.'
    
    function getMessage(fieldName: string, msgID: string): string {
       // Templating magic happens here, Regex or whatever other method you prefer
    }
    ...
    
    body('username')
            .isString()
            .withMessage(getMessage('username', MSG_TYPE_STR)
    
    ....
    

    This way, you will still get nicely formatted error messages and you would not have copy-paste the same string over and over again

    Approach 2

    You could conceivably add post-processing to the validation error (i.e. take the ugly output and do some calculations and transform it to a nicer output). This would be a considerably harder approach, as you would have to break down the rules to atomic checks and your validation scheme becomes very long, and still it's a fair bit of work

    Approach 3

    This is not so much an "approach" as a suggestion - you may switch to a different validator, like Joi for example, which has a much more reasonable default logic around error messages