Creating your first JOI extension

Shubham Zanwar
2 min readJun 29, 2019

--

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! ☕️

--

--