I'd like to describe one core model with all data type validation and extend that model in multiple places with different key require/optional values.
'use strict';
const Joi = require('joi');
var Person = Joi.object({
name: Joi.string(),
height: Joi.number(),
favoriteMovies: Joi.array()
});
var MovieLover = Person.keys({
favoriteMovies: Joi.required()
});
var MeasuredPerson = Person.keys({
height: Joi.required()
});
MovieLover.describe() response:
{ type: 'object',
children:
{ name: { type: 'string', invalids: [ '' ] },
height: { type: 'number', invalids: [ Infinity, -Infinity ] },
favoriteMovies: { type: 'array', flags: { sparse: false, presence: 'required' } } } }
{ type: 'object',
children:
{ name: { type: 'string', invalids: [ '' ] },
height: { type: 'number', invalids: [ Infinity, -Infinity ] },
favoriteMovies: { type: 'any', flags: { presence: 'required' } } } }
If this is not possible, is there another way to achieve this behavior? The modifications I would like to make are purely require/optional based. So exporting, add the presence: 'required' flag, and re-importing would be an option.
Furthermore, if that too is not possible and could be added as an enhancement how would that be structured? I am willing to make this contribution to the project myself.
You might look into usage of Joi.any().concat() https://github.com/hapijs/joi/blob/master/API.md#anyconcatschema
Correct answer @devinivy, keys replaces, concat merges.
The documentation doesn't make this (amazing) feature entirely clear. Including the end result should a future reader come across this:
'use strict';
const Joi = require('joi');
var Person = Joi.object({
name: Joi.string(),
height: Joi.number(),
favoriteMovies: Joi.array()
});
var MovieLover = Person.concat(Joi.object({
favoriteMovies: Joi.required()
}));
var MeasuredPerson = Person.concat(Joi.object({
height: Joi.required()
}));
Yeah, I had no clue about that feature either. It's definitely documented in a succinct and easy to understand way right now here though.
Just one other question that isn't entirely related to this issue but also can't find an answer to. Is there a way to set other keys to unknown on a concat'd schema?
i.e.
var MeasuredPerson = Person.concat(Joi.object({
height: Joi.required()
})).unknown(false);
which would inherit the validation info for height AND prevent keys other than height.
Don't try to think object-oriented, you're building validation schemas, you don't need to always use Person.
@Marsup the issue is we had been doing things like that but we ended up with far too many files to keep track of and duplicated data was not always being updated properly.
Are those in separate files ? A little context might help me provide a better answer :)
Yes. We have separate files for each response schema at the moment. For now we're going to use the following which works fine.
var MeasuredPerson = Person.concat(Joi.object({
height: Joi.required(),
favoriteMovies: Joi.forbidden(),
name: Joi.forbidden()
}));
Then you might extract what you need from this, but at some point you'll find it easier to just export the interesting parts from your Person schema :
var MeasuredPerson = Joi.object({
height: Joi.reach(Person, 'height').required()
});
Most helpful comment
The documentation doesn't make this (amazing) feature entirely clear. Including the end result should a future reader come across this: