joi errors not overriding for nested objects

Created on 15 Jun 2018  路  7Comments  路  Source: sideway/joi

Context

  • node version: 8.x
  • joi version: latest
  • environment (node, browser): node
  • used with (hapi, standalone, ...): standalone

What are you trying to achieve or the steps to reproduce ?

const userSchema = Joi.object().keys({
  city: Joi.object().keys({
    id: Joi.number().required().error(new Error('invalid city id')),
  }).required().error(new Error('city is required')),
});

Which result you had ?

it returns 'city is required' unless all the nested objects are valid.

What did you expect ?

display error for each part.

while this works without .error(), I want to be able to send custom error messages for each part.

For example:

const userSchema = Joi.object().keys({
  city: Joi.object().keys({
    id: Joi.number().required().error(new Error('invalid city id')),
  }).required().error(new Error('city is required')),
});

userSchema.validate({ city: { id: 'asfasf' } }); // returns 'city is required', while I'm expecting it to return 'invalid city id'
support

Most helpful comment

Added a new syntax in 14.2.0 to support that more easily if you're interested.

Can you provide a direct link to that new syntax?

All 7 comments

Duplicate of https://github.com/hapijs/joi/issues/1219. I know it's confusing but it's documented.

Even applying abortEarly: false, it still does not override that error. What's the workaround on this?

code:

const Joi = require('joi');

const schema = Joi.object().keys({
  city: Joi.object().keys({
    id: Joi.number().required().error(new Error('invalid city')),
  }).required().error(new Error('city is required'))
});

console.log(schema.validate({ city: { id: 'asd' } }, { abortEarly: true }).error); // returns `city is required` instead of `invalid city`

Seems no response so far, as a workaround, I wrote a helper function which gets labels instead and prints them (only first item in my case, no need multiple).

P.S using ramda & ramda-fantasy libraries.

const getProp = R.useWith(R.path, [R.split('.')]);
const maybeGetProp = R.curry((prop, obj) => F.Maybe(getProp(prop, obj)));
const maybeGetJoiError = R.pipeK(maybeGetProp('error.details'), x => F.Maybe(R.head(x)), maybeGetProp('context.key'));
const userSchema = joi.object().keys({
  city: joi.object().keys({
    id: joi.number().required().label('invalid city'),
  }).required().label('city is required')
});

const res = userSchema.validate({ city: {} }, { abortEarly: false });

maybeGetJoiError(res); // returns Maybe.Just({ value: 'invalid city' }) or Maybe.Nothing().

Let me know if it's correct approach for labels (e.g parsing correctly).

Thanks.

I think you missed the complexity of error(), maybe the documentation is not enough there. You can see joi schemas as big nested try/catch blocks that can throw several types of errors. So in your original code, id was throwing 'invalid city id' because of type mismatch, it was then caught by city which turned it into 'city is required' without even considering what happened.

A working example would be:

const schema = Joi.object().keys({
    city: Joi.object().keys({
        id: Joi.number().required().error(new Error('invalid city')),
    }).required().error((errors) => {
        // If at least one error comes from city itself
        if (errors.some(e => e.context.key === 'city')) {
            return new Error('city is required');
        }
        return errors;
    })
});

Ahh.. Now it's all clear. Thank you!

Closing this.

Added a new syntax in 14.2.0 to support that more easily if you're interested.

Added a new syntax in 14.2.0 to support that more easily if you're interested.

Can you provide a direct link to that new syntax?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JbIPS picture JbIPS  路  4Comments

Dreamystify picture Dreamystify  路  4Comments

alekbarszczewski picture alekbarszczewski  路  3Comments

REBELinBLUE picture REBELinBLUE  路  3Comments

jamesdixon picture jamesdixon  路  4Comments