Creating your first JOI extension
So this is going to be a super short primer for people who’re trying to write JOI extensions. I recently wrote an extension to validate mongodb object IDs and I’ll document the steps for the same here; Let’s get started
Joi uses something called as extension
objects to “extend” its functionality.
These objects typically look something like this:
{
base: any_joi_base_function_you_want_to_use,
name: 'the_name_of_your_validation',
language: { // This is where you'd store error messages
'key_name': 'Error messages',
},
pre(value, state, options) {
you can parse the object value here or
even add a base check and throw error
},
rules: [{
name: 'name_of_sub_validation',
validate(params, value, state, options) {
if (some_condition_doesn't_match) {
return this.createError(
'path_to_err_in_language',
{ v: value },
state,
options
);
}
return value;
},
}, ...more_rules],
}
now, we can either pass this object as an argument to the Joi.extend
method or create a function that returns an object like that 👆
Joi says you can also pass an array of a combination of the two; I haven’t tried it.
Coming to the object-id extension, we know that object-ids are essentially strings. So we can use the Joi.string
methods as the base. For the name, let’s call it dbId
. Why? We could club multiple rules
here. Say you also have a postgres table that uses UUID
as ids. 🤷♂
For now, though, let’s stick to creating just one rule; for mongoDb, mongoid
.
How do we validate whether a string is a valid mongo id?
Simple; If you’re using mongoose
as your ODM, it exposes a function where you can test whether a value is a valid Mongo Type
. Read more about it here.
The function we can use here is the mongoose.Types.ObjectId.isValid
. As obvious, it returns a true/false depending on the value passed.
Plugging all the pieces together
Now, we have everything required to create our first extension object factory method. It should look something like this:
const extention = joi => ({
base: joi.string(),
name: 'dbId',
language: {
mongoid: 'needs to be a valid object id',
},
rules: [{
name: 'mongoid',
validate(params, value, state, options) {
if (!Mongoose.Types.ObjectId.isValid(value)) {
return this.createError(
'dbId.mongoid',
{ v: value },
state,
options
);
}
return value;
},
}],
});
Now all you’ve got to do is extend the original Joi object with the factory method and you’re good to go 👍
const Joi = require('@hapi/joi');const CustomJoi = Joi.extend(extention);// You can now use CustomJoi.dbId().mongoid() as a validation in your schemas!
Yep; that’s all there is to it;
You can find the code and npm link for this package here:
Cheers! ☕️