hapijs / joi

The most powerful data validation library for JS
Other
20.95k stars 1.51k forks source link

Can't use validateAsync is giving error if no external validation rule #3000

Closed yachtsman-dev closed 1 year ago

yachtsman-dev commented 1 year ago

Runtime

node.js

Runtime version

18.17.1

Module version

17.11.0

Used with

nodejs

Any other relevant information

I am using validate middleware created by hapi https://github.com/hagopj13/node-express-boilerplate/blob/master/src/middlewares/validate.js In my validation schema i have mixed validations external and normal rules. when i try to validateAsync it's giving promise error like node:internal/process/promises:288 triggerUncaughtException(err, true /* fromPromise */);

Is there any way i can separate the external and other validation and validate using middleware?

How can we help?

const validate = (schema, useJoiError = true) => (req, res, next) => {
  const validSchema = pick(schema, ['params', 'query', 'body']);
  const object = pick(req, Object.keys(validSchema));
  const { value, error } = Joi.compile(validSchema)
    .validateAsync(object, { errors: { label: 'key' }, abortEarly: false, allowUnknown: true, stripUnknown: true });

  if (error) {
    // Joi Error
    const JoiError = error.details.map(({ context, message }) => ({
      [context.label]: message.replace(/['"]/g, ''),
    }))

    return JoiError
  }
  Object.assign(req, value);
  return next();
};
Nargonath commented 1 year ago

Are you sure the validate middleware comes from hapi? I doubt since hapi doesn't have a concept of middleware. Looks like a community made utility to me but I may be wrong.

Is the pick function from lodash? Can you provide your schema so we can see how it's made, please?

yachtsman-dev commented 1 year ago

Are you sure the validate middleware comes from hapi? I doubt since hapi doesn't have a concept of middleware. Looks like a community made utility to me but I may be wrong.

Is the pick function from lodash? Can you provide your schema so we can see how it's made, please?

No pick function is just extract data from Object. see below

const pick = (object, keys) => { return keys.reduce((obj, key) => { if (object && Object.prototype.hasOwnProperty.call(object, key)) { // eslint-disable-next-line no-param-reassign obj[key] = object[key]; } return obj; }, {}); };

I have mentioned from where i have used this middleware, which is one of popular node boiler plate. https://github.com/hagopj13/node-express-boilerplate/blob/master/src/middlewares/validate.js

Nargonath commented 1 year ago

Thank you for the details. I noticed what you said because you mentioned it's from hapi, that's all. I don't think it's related to hapi though.

Can you provide me an example of a schema object (the one received by the middleware validate function) you used where you get the error, please?

yachtsman-dev commented 1 year ago

Can you provide me an example of a schema object (the one received by the middleware validate function) you used where you get the error, please?

{ body: Joi.object().keys({ Number: Joi.number().external(verifyNumber), TotalAmount: Joi.number().positive().greater(0).required(), Email: Joi.string().email().required(), Currency: Joi.string().required(), ExpiryMonth: Joi.number().when('type', { is: 'card', then: Joi.number().max(12).min(1).required() }), ExpiryYear: Joi.number().when('type', { is: 'card', then: Joi.number().min(moment().year()).required() }) }), }

In above Schema verifyNumber is my db function where i am verify number with database and return true if success else returning error message. As mentioned above if i use validateAsync then external validation work but other validation are giving error as mentioned in above details. If i use only validate instead of validateAsync in middleware then it's giving me error Schema with external rules must use validateAsync(). So now my problem is how can i use external and nomal validation both by using above middleware?

const verifyNumber = async (value, helpers) => { try { const res = db function to check record and it's returning true or false; if (res) { return value } else { return helpers.message("Can't verify number, please contact us.") } } catch (error) { return helpers.message("Can't verify number, please contact us.") } };

Marsup commented 1 year ago

validateAsync returns a promise, you can't use it like that without await or chaining with a then. I don't know express well, but I know it doesn't support promises. If I were you, I'd look for solutions on how to use middlewares with promises, it's not an issue with joi.

yachtsman-dev commented 1 year ago

validateAsync returns a promise, you can't use it like that without await or chaining with a then. I don't know express well, but I know it doesn't support promises. If I were you, I'd look for solutions on how to use middlewares with promises, it's not an issue with joi.

I see, i put async await and it seems working. But only issue is if error on external validation then it's not giving errors with other validation errors. Once all other validations are fix then on next try it's giving error for external validation.

Nargonath commented 1 year ago

I believe that's because external validations are run only once all other validations are passed. This is per design as explained in the documentation:

Note that external validation rules are only called after the all other validation rules for the entire schema (from the value root) are checked. This means that any changes made to the value by the external rules are not available to any other validation rules during the non-external validation phase.

If schema validation failed, no external validation rules are called.

Source: https://joi.dev/api/?v=17.9.1#anyexternalmethod-description

It seems @Marsup's suggestion fixed your initial problem so I'm going to close this issue. Let us know if there is still something related to joi that's bugging you.