javascriptexpresscallbackmiddlewareexpress-validator

How can a function return an array of validators and also call next()?


I need a function that takes the request body and conditionally creates the validators that will be used on the request. I figured the best way to do this is by creating middleware but I'm running into a problem with express-validator. For express-validator to work the middleware has to return an array of my validators but if I do this I can't call next() and the request doesn't get handled.

// This will fail because I can't call next
const validatorMiddleware = (req, res, next) => {
  const {
    isFooRequired,
    isBarRequired,
  } = req.body;

  return concat(
    isFooRequired && createValidator('foo'),
    isBarRequired && createValidator('bar'),
  )
 }

With the way I'm going about this I basically need a function that calls next() AND returns the array of validators.

I understand express-validator has a way to conditionally add validation rules but there doesn't seem to be an easy way to do this in bulk. I'd have to do this individually with each validation chain which is a huge pain and kind of gross if you ask me. I have several optional fields which are dependent on a lot of validation and I want to do something like this:

  const addressValidators = (service) => [
    body(`${service}.address.lineOne`).notEmpty().withMessage('Address cant be empty'),
    body(`${service}.address.city`).notEmpty().withMessage('city cant be empty'),
    body(`${service}.address.state`).isIn(allowedStates).withMessage('invalid state'),
  body(`${service}.address.zipCode`).isPostalCode('US').withMessage('invalid zip code'),
];

Solution

  • In case anyone has this same problem I do, here's the solution I found. With express-validator you can run validations imperatively so I created the middleware you see below. Inside my constructValidators function I use the request body to see which fields are present and create my validation chain accordingly.

    const validateMiddleware = async (req, res, next) => {
    const validationList = constructValidators(req.body);
    
    await Promise.all(validationList.map((validation) => validation.run(req)));
    
    const errors = validationResult(req);
    if (errors.isEmpty()) {
      return next();
    }
    
    res.status(400).json({ errors: errors.array() });
    return null;
    

    };

    Remember, 3 hours of troubleshooting could save you 10 minutes of reading documentation!