javascriptnode.jsvalidationjoi

Joi Validations: If object matches the schema validate against it from multiple items


I am having an array of objects to validate from the specific schemas and if any of the objects matches with the given schemas, I want to validate against it and ignore other schemas and move to next array item.

I tried Joi.alternatives but it will go and check against all the schemas instead of checking only one if it got matched.

My validator:

Joi.array().min(1).max(49).items(Joi.object({
    type: Joi.number().valid([1, 2, 3, 4]).required()
}).when(Joi.object({
    type: Joi.number().valid(1)
}).unknown(), {
    then: profile1
}).when(Joi.object({
    type: Joi.number().valid(2)
}).unknown(), {
    then: profile2
}).when(Joi.object({
    type: Joi.number().valid(3)
}).unknown(), {
    then: profile3
}))

Profile1//type1

export default Joi.object().keys({

    type: Joi.number().valid([1, 2, 3]).required(),
    surname: Joi.string().max(50).allow(""),
    givenname: Joi.string().max(50).allow("")

}).unknown(true)

Profile2//type2

export default Joi.object().keys({

    type: Joi.number().valid([1, 2, 3]).required(),
    address: Joi.string().max(50).allow(""),
    initialname: Joi.string().max(50).allow(""),
    surname: Joi.string().max(50).allow(""),
    data: Joi.array.min(1).max(29).items(Joi.object({
       code: Joi.string().max(20).allow(""),
       number: Joi.string().max(20).allow(""),
    }),
    address1: Joi.array.min(1).max(29).items(Joi.object({
       city: Joi.string().max(20).allow(""),
       postal: Joi.string().max(20).allow(""),
    })
}).unknown(true)

Profile3//type3

export default Joi.object().keys({

    type: Joi.number().valid([1, 2, 3]).required(),
    units: Joi.string().max(50).allow(""),
    others: Joi.string().max(50).allow(""),
    surname: Joi.string().max(50).allow(""),
    data2: Joi.array.min(1).max(29).items(Joi.object({
       sub1: Joi.string().max(20).allow(""),
       sub2: Joi.string().max(20).allow(""),
    }),
    additional: Joi.array.min(1).max(29).items(Joi.object({
       data1: Joi.string().max(20).allow(""),
       data2: Joi.string().max(20).allow(""),
    })

}).unknown(true)

sample object:

[
   {
      "type":1,
      "surname":"",
      "givenname":""
   },
   {
      "type":2,
      "surname":"val",
      "address":"testte",
      "initialname":"test",
      "data":[{
         "code":"test",
         "number":"test"
      }],
      "address1":[{
         "city":"test",
         "postal":123
      }
   }],
   {
      "type":"3",
      "surname":"",
      "units":"",
      "others":"",
      "data2":[{
         "sub1":"test",
         "sub2":"test"
      }],
      "additionals":[{
         "data1":"test",
         "data2":123
      }]
   }
]

here profile1 gets matched with given data so it should validate against it and ignore profile2 and profile3 (also validate type is number)

here profile2 gets matched with given data so it should validate against it and ignore profile1 and profile3 (also validate type is number)

now it should move to the next object from the array where type is not number so it should not check schema and throw an error

Note: I am using Joi version 13.4.0


Solution

  • There are some typos in your files and validators.

    First of Joi.array is a function and needs to be called as one.

    // This is incorrect
    data2: Joi.array
    ...
    additional: Joi.array
    
    // Should be changed to this where array is invoked as a function
    data2: Joi.array()
    ...
    additional: Joi.array()
    

    Additionally, I've added a fourth validator which basically passes all sample objects which have a type greater than 3.

    .when(
      Joi.object({
        type: Joi.number().greater(3),
      }).unknown(),
      {
        then: Joi.object().unknown(true),
      }
    )
    

    And there are other typos in your validators e.g missing brackets - I created a fixed version of your validator in codesandbox.

    Validation is still failing in the codesandbox since in profile2.js postal is declared as string, but in your samples array it is a number for the second object, search for postal: 123 and change it to postal: "123" or modify the validation of postal from postal: Joi.string() to postal: Joi.number()

    This way if one of the validators passes then the other ones won't throw an error, and if the type is greater than 3 it will pass validation.