Search code examples
typescriptexpressvalidationprismaexpress-validator

.bail() doesn't work with express-validator


I am registering a user I need a name, email and a password. When there is no name given the backend does not need to check if the email is valid.

To my knowledge this is where the .bail() functionality comes in of the express-validator. This functionality unfortunately does nothing in my case all validations run anyway.

The route: app.post('/register', validate(createUserSchema), createUser);

The validate() function:

export const validate = (schemas: ValidationChain[]) => async (req: Request, res: Response, next: NextFunction) => {
   await Promise.all(schemas.map(async (schema) => await schema.run(req)));

   const result = validationResult(req);
   if (result.isEmpty()) {
       return next();
   }

   const errors = result.array();
   return res.status(422).json(errors);
};

The createUserSchema:

export const createUserSchema = [
    body('name', 'Name is required').notEmpty().bail(),
    body('email', 'Email is required').notEmpty().bail(),
    body('email').isEmail().withMessage('Email is not valid').bail(),
    body('email')
        .custom((value) => {
            if (!value) return true;
            return prisma.user
                .findUnique({
                    where: {
                        email: value
                    }
                })
                .then(async (user) => {
                    if (user) {
                        return await Promise.reject(new Error('E-mail already in use'));
                    }
                    return await Promise.resolve();
                });
        }).bail(),
    body('password', 'Password is required').notEmpty().bail()
        .isLength({min: 6}).withMessage('Password must be longer than 6 characters')
        .matches(/[A-Z]/).withMessage('Password does not contain an uppercase character')
        .matches(/\W/).withMessage('Password does not contain any non-word characters')
];

my request payload:

{
    name: "", 
    email: "",
    password: ""
}

The response I get:

[
    {
        "value":"",
        "msg":"Name is required",
        "param":"name",
        "location":"body"
    },
    {
        "value":"",
        "msg":"Email is required",
        "param":"email",
        "location":"body"
    },
    {
        "value":"",
        "msg":"Email is not valid",
        "param":"email",
        "location":"body"
    },
    {
        "value":"",
        "msg":"Password is required",
        "param":"password",
        "location":"body"
    }
]

The response I expect (because it should bail on the first validation):

[
    {
        "value":"",
        "msg":"Name is required",
        "param":"name",
        "location":"body"
    }
]

Am I doing something wrong?


Solution

  • Here to answer my own question -_-

    So I misunderstood what a Validation Chain is in express-validator.

    I was under the impression that different validations rows formed one validation chain (so an array woud form a chain). This is not the case. A chain is formed by the separate validation rules.

    For example

    export const createUserSchema = [
        body('name').notEmpty().withMessage('Name is required').bail(), //<--- this is a validation chain
        body('email').notEmpty().withMessage('Email is required').bail()
            .isEmail().withMessage('Email is not valid').bail()
    ]; // <--- The whole array does not form a chain (which is what I thought)
    

    Gustavo Henke explained it here.

    So to achieve what I want I needed to use the .if(condition) validation.