pkout
pkout

Reputation: 6736

Joi validate individual fields of nested schema

I have a nested schema setup like this:

var schema = Joi.object().keys({
  first_name: Joi.string().required(),
  last_name: Joi.string().required(),
  address: Joi.object().keys({
    street: Joi.string().required(),
    zip: Joi.number().required(),
    state: Joi.string().required(),
    country: Joi.string().required()
  }).required(),
})

var options = { abortEarly: false };

var result = Joi.validate({}, schema, options);

When running this, result.error will return the message:

[
  {message: '"first_name" is required', path: 'first_name' }, 
  {message: '"last_name" is required', path: 'last_name' },
  {message: '"address" is required', path: 'address' }
]

As you see, the address field is not expanded. I don't want to get a message that the whole address field is missing. Instead, I want to be informed about the individual items in the address schema missing. When I don't make address explicitly required, the items in it don't get validated against required() at all. The nested field validation seems to work only on the entire nested schema object level, not on the individual items within the schema level. How do I make it work with the individual nested schema items?

Upvotes: 4

Views: 2482

Answers (1)

Ashley Coolman
Ashley Coolman

Reputation: 11585

Adding a "skeleton" object works. Working example here.

import Joi, {
  string,
  number,
  object
} from "joi";

var schema = object({
  first_name: string().required(),
  last_name: string().required(),
  address: object({
    street: string().required(),
    zip: number().required(),
    state: string().required(),
    country: string().required()
  })/*.required(), <= Not needed */
})
var skeleton = { address: {} };

var options = { abortEarly: false };

var validate = data => {
  var newData = Object.assign({}, skeleton, data);
  return Joi.validate( newData, schema, options);
}
console.log(
  JSON.stringify(validate({}).error.details, null, 2)
);

Result:

[
  {
    "message": "\"first_name\" is required",
    "path": "first_name",
    "type": "any.required",
    "context": {
      "key": "first_name"
    }
  },
  {
    "message": "\"last_name\" is required",
    "path": "last_name",
    "type": "any.required",
    "context": {
      "key": "last_name"
    }
  },
  {
    "message": "\"street\" is required",
    "path": "address.street",
    "type": "any.required",
    "context": {
      "key": "street"
    }
  },
  {
    "message": "\"zip\" is required",
    "path": "address.zip",
    "type": "any.required",
    "context": {
      "key": "zip"
    }
  },
  {
    "message": "\"state\" is required",
    "path": "address.state",
    "type": "any.required",
    "context": {
      "key": "state"
    }
  },
  {
    "message": "\"country\" is required",
    "path": "address.country",
    "type": "any.required",
    "context": {
      "key": "country"
    }
  }
]

Upvotes: 1

Related Questions