Joi: Using when() to require one or more object keys

Created on 8 Aug 2018  路  5Comments  路  Source: sideway/joi

Context
Node version: 6.13.1
Joi version: 13.3.0

Problem
Good day folks, I'm attempting to create a validation schema for an object that varies based on the value of one of its keys. When said key's value is set to certain string then some other keys are required. Here's a slightly contrived example:

const animalSchema = Joi.object()
  .keys({
    type: Joi.string().only(['MAMMAL', 'REPTILE']),
    name: Joi.string()
  })
  .unknown(false)
  .requiredKeys(['type', 'name']);

const reptileSchema = Joi.any()
  .concat(animalSchema)
  .keys({
     coldBlooded: Joi.boolean().only(true),
     laysEggs: Joi.boolean().only(true),
  })
  .requiredKeys(['coldBlooded', 'laysEggs']);

animalSchema.when(
  'type',
  {
    is: 'REPTILE',
    then: reptileSchema,
  }
);

const input = {
  type: 'REPTILE',
  name: 'Snake'
};

const err = animalSchema.validate(input).error; // returns null but ValdiationError is expected as keys: ['coldBlooded', 'laysEggs'] should be required.

Would love if you guys can provide some insight on this. Thanks in advance.

support

Most helpful comment

Your choice, either as separate schemas:

const animalSchema = Joi.object()
    .keys({
        type: Joi.string().only(['MAMMAL', 'REPTILE']).required(),
        name: Joi.string().required()
    });

const reptileSchema = Joi.object()
    .keys({
        coldBlooded: Joi.boolean().only(true).required(),
        laysEggs: Joi.boolean().only(true).required()
    });

const schema = animalSchema
    .when(
        Joi.object({ type: 'REPTILE' }).unknown(),
        {
            then: reptileSchema
        }
    );

Or as a single schema:

const schema = Joi.object()
    .keys({
        type: Joi.string().only(['MAMMAL', 'REPTILE']).required(),
        name: Joi.string().required(),
        coldBlooded: Joi.when('type', { is: 'REPTILE', then: Joi.boolean().only(true).required(), otherwise: Joi.forbidden() }),
        laysEggs: Joi.when('type', { is: 'REPTILE', then: Joi.boolean().only(true).required(), otherwise: Joi.forbidden() })
    });

All 5 comments

Your choice, either as separate schemas:

const animalSchema = Joi.object()
    .keys({
        type: Joi.string().only(['MAMMAL', 'REPTILE']).required(),
        name: Joi.string().required()
    });

const reptileSchema = Joi.object()
    .keys({
        coldBlooded: Joi.boolean().only(true).required(),
        laysEggs: Joi.boolean().only(true).required()
    });

const schema = animalSchema
    .when(
        Joi.object({ type: 'REPTILE' }).unknown(),
        {
            then: reptileSchema
        }
    );

Or as a single schema:

const schema = Joi.object()
    .keys({
        type: Joi.string().only(['MAMMAL', 'REPTILE']).required(),
        name: Joi.string().required(),
        coldBlooded: Joi.when('type', { is: 'REPTILE', then: Joi.boolean().only(true).required(), otherwise: Joi.forbidden() }),
        laysEggs: Joi.when('type', { is: 'REPTILE', then: Joi.boolean().only(true).required(), otherwise: Joi.forbidden() })
    });

@Marsup Thanks, this helped a lot. I was able to get this to work for the use case I had in mind, however, there were a few things I noted:

  • I needed to use Joi.object(...).unknown() as the condition instead of the combination of the property name and is. Anything to note here? I got this idea from the docs, did I misunderstand something?
  • when using separate schemas, I noted schema.when creates a new schema instead of modifying the existing one. I assumed the existing schema was modified, but not mutating it is a good move as it makes it a little easier to share base schemas.
  • I don't understand the question, it's either a schema as condition, or is, not both.
  • It's written in the readme that schemas are immutable, I should probably duplicate that info in the API.

I don't understand the question, it's either a schema as condition, or is, not both.

Just to clarify, I wasn't able to get this to work:

const schema = animalSchema
  .when(
    'type',
     {
       is: 'REPTILE',
       then: reptileSchema
     }
  );

So maybe there's something I'm missing regarding the use of is.

It's written in the readme that schemas are immutable, I should probably duplicate that info in the API.

Ah I see, I glossed over that section.

This thread has been automatically locked due to inactivity. Please open a new issue for related bugs or questions following the new issue template instructions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

leore picture leore  路  4Comments

jamesdixon picture jamesdixon  路  4Comments

Taxi4you picture Taxi4you  路  3Comments

neroaugustus1 picture neroaugustus1  路  4Comments

sergibondarenko picture sergibondarenko  路  3Comments