Joi arr conditional length

Viewed 382

A user will be sending contact information as an array of objects like the following, with the phone number being optional and the email being required:

{
  "contact": [{
    "type": "phone",
    "value": "555-555-5555"
  }, {
    "type": "email",
    "value": "test@test.com"
  }]
}

I would like to ensure there is an email object inside the array. I tried a Joi validation like this:

contact: Joi.array().items(Joi.object().keys({
      type: Joi.string().valid('phone', 'email'),
      value: Joi.string()
        .when('contact.type', { is: 'phone', then: Joi.string() })
        .when('contact.type', { is: 'email', then: Joi.string().email().required() })
    }))
      .when('contact.type', { is: 'phone', then: Joi.array().min(2).required() }),

But I get the following error:

Error: Item cannot come after itself: contact

It seems it doesn't like me giving it a length in this manner but I can't figure out any other way to do it. Any help would be appreciated. Thanks.

2 Answers

This schema combining .when, .has, and .unique, should work:

Joi.object({
  contact: Joi.array().items(
    Joi.object().keys({
      type: Joi.string().valid('phone', 'email').required(),
      value: Joi.string().when('type', { is: 'email', then: Joi.required() })
    }).required(),
  )
  .has(Joi.object({ type: 'email', value: Joi.exist() }))
  .unique('type').min(1).max(2)
})

Let's see the rules:

  1. the object with the type 'email' must exist;

That's why I've added

.has(Joi.object({ type: 'email', value: Joi.exist() }))

This means that the array must have at least one of these elements.

  1. We don't want duplicates, right?
.unique('type').min(1).max(2)

The array will have either 1 element, or 2, with different types.

  1. When the type is 'email', the value should be required, and optional otherwise. That's what we are saying here:
value: Joi.string().when('type', { is: 'email', then: Joi.required() })

Thank you @soltex for your answer.

First I'm not sure we should exclude duplicates. Some people have multiple phone numbers.

Second, your answer did not quite work. Here is my updated answer based on what you wrote:

contact: Joi.array().items(Joi.object().keys({
      type: Joi.string().valid('phone', 'email').required(),
      value: Joi.string().required(),
    }))
    .has(Joi.object({ type: 'email', value: Joi.string().email() }))

Once I include the .has method then there is no reason to include the .when method. I also want the email to be a valid email not just that it exists. I changed value to required because if the user sends type phone I still want them to include the value.

Once again thanks for your guidance.

Related