Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Joi validator conditional schema

I need to create dynamic schema to validate my api request query in node js using Joi validator depending on a key in the request query. Say the following below mentioned pattern are my valid queries.

I'm using hapi/joi version 16.1.8

Combination 1

{ type: 1, firstname: 'user first name', lastname: 'user last name'}

Combination 2

{ type: 2 , salary: 1000, pension: 200}

Combination 3

{ type: 3 , credit: 550, debit: 100}

As you can see the object keys varies depending on the value of type. How this can be handled properly?

We can handle two conditions using Joi.alternatives like

const schema = Joi.alternatives().conditional(Joi.object({ type: 1 }).unknown(), {
    then: Joi.object({
        type: Joi.string(),
        firstname: Joi.string(),
        lastname: Joi.string()
    }),
    otherwise: Joi.object({
        type: Joi.number(),
        salary: Joi.any(),
        pension: Joi.any()
    })
});

But how this can be done for 3 conditions?

like image 622
Nitheesh Avatar asked Jan 22 '20 14:01

Nitheesh


People also ask

What is schema validation middleware in Joi?

Schema validation middleware for create account requests in Node.js with joi This example Node.js middleware function validates a request to create a new account. It defines schema rules with the joi schema validation library and calls schema.validate (req.body, options) to determine if the request is valid.

How do I validate the data in Joi?

The validation is done using the Joi.validate () method, with the following signature: data: the data to validate which in our case is req.body. schema: the schema with which to validate the data. options: an object that specifies the validation options. Here are the validation options we used:

How to create validation schemas for the API routes in Joi?

After familiarizing yourself with constraints and schemas in Joi, you can now create the validation schemas for the API routes. Create a new file named schemas.js in the project route directory: Start by requiring Joi: The /people endpoint will use personDataSchema. In this scenario, an administrator is creating accounts for teachers and students.

How do I use alternative schemas in Joi?

Sometimes, you may want a value to be either a string or number or something else. This is where alternative schemas come into play. You can define alternative schemas using Joi.alternatives (). It inherits from the any () schema, so constraints like required () can be used with it.


3 Answers

I achieved the same in a little different manner. Posting the same here since this might be useful for someone in future.

const schema = Joi.object({
    type: Joi.number().required().valid(1, 2, 3),
    firstname: Joi.alternatives().conditional('type', { is: 1, then: Joi.string().required() }),
    lastname: Joi.alternatives().conditional('type', { is: 1, then: Joi.string().required() }),
    salary: Joi.alternatives().conditional('type', { is: 2, then: Joi.number().required() }),
    pension: Joi.alternatives().conditional('type', { is: 2, then: Joi.number().required() }),
    credit: Joi.alternatives().conditional('type', { is: 3, then: Joi.number().required() }),
    debit: Joi.alternatives().conditional('type', { is: 3, then: Joi.number().required() }),
}))

This was working perfectly as expected.

When the type value is 1 the object should have only type, firstname and lastname

When the type value is 2 the object should have only type, salary and pension

When the type value is 3 the object should have only type, credit and debit

Any other combination will be thrown as error from the joi validator middleware layer. Also any other type value other that 1, 2 and 3 will be throwing error.

like image 67
Nitheesh Avatar answered Oct 16 '22 20:10

Nitheesh


It works for me!

var Joi = require('joi');

var schema = {
    a: Joi.any().when('b', { is: 5, then: Joi.required(), otherwise: Joi.optional() }),
    b: Joi.any()
};

var thing = {
    b: 5
};
var validate = Joi.validate(thing, schema);

// returns
{
    error: null,
    value: {
        b: 5
    }
}

This is the reference.

like image 17
soamazing Avatar answered Oct 16 '22 20:10

soamazing


In the documentation it look like switch is valid key to use along alternatives.conditional. Could you try the following ?

const schema = Joi.alternatives().conditional(Joi.object({
  type: 1
}).unknown(), {
  switch: [{
    is: 1,

    then: Joi.object({
      type: Joi.string(),
      firstname: Joi.string(),
      lastname: Joi.string(),
    }),
  }, {
    is: 2,

    then: Joi.object({
      type: Joi.number(),
      salary: Joi.any(),
      pension: Joi.any(),
    }),
  }, {
    // ...
  }],
});

EDIT :

Couldn't find any example anywhere about the use of the switch keyword...

But found some other way to achieve it in hapijs/joi github

const schema = Joi.object({
     a: Joi.number().required(),
     b: Joi.alternatives()
             .conditional('a', [
                 { is: 0, then: Joi.valid(1) },
                 { is: 1, then: Joi.valid(2) },
                 { is: 2, then: Joi.valid(3), otherwise: Joi.valid(4) }
    ])
});
like image 6
Orelsanpls Avatar answered Oct 16 '22 19:10

Orelsanpls