Search code examples
javascriptnode.jsjoi

JOI validation for a string with json as a value


i am trying to validate the string using the JOI package available in npm, i checked this documentation which has many useful string formats e.g. date, IP, base64 but i need to validate the following JSON which contains a stringified JSON as a value, and there is no example in the documentation for such case

{
    "id": 232,
    "name": "Trojan Horse",
    "file": "download.exe",
    "infected": true, 
    "engines": "['Norton', 'AVG', 'NOD32']"
}

So for example what if i want to check engines have valid JSON value and have at-least one engine defined if infected key is set to true

The following schema works only if the engines value is written as parsed JSON

Joi.object().keys({
    id: Joi.number().required(),
    name: Joi.string().min(5).required(),
    file: Joi.string().min(3).required(),
    infected: Joi.boolean().required(),
    engines: Joi.array().when('infected', {
        is: Joi.exists().valid(true),
        then: Joi.min(1).required()
    })
});

Solution

  • What you need to do is to create a custom JOI validator by extending the array validator of the JOI package and using that custom validator for the engines property.

    const custom = Joi.extend({
    type: 'array',
    base: Joi.array(),
    coerce: {
          from: 'string',
          method(value, helpers) {
    
              if (typeof value !== 'string' ||
                  value[0] !== '[' && !/^\s*\[/.test(value)) {
    
                  return;
              }
    
              try {
                return { value: JSON.parse(value) };
              }
              catch (ignoreErr) { }
          }
      }
    });
    
    const schema = Joi.object({
      id: Joi.number().required(),
      name: Joi.string().min(5).required(),
      file: Joi.string().min(3).required(),
      infected: Joi.boolean().required(),
      engines: custom.array().when('infected', {
          is: true,
          then: custom.array().min(1).required()
      })
    })
    
    const validateTest = async (joiSchema,  testObject) => {
      try {
        const value = await joiSchema.validateAsync(testObject);
        console.log(value);
    }
    catch (err) { 
      console.error(err)
     }
    };
    
    validateTest(schema, {
      "id": 232,
      "name": "Trojan Horse",
      "file": "download.exe",
      "infected": true, 
      "engines": `["Norton", "AVG", "NOD32"]`
    })
    

    You can see more examples like that here