31

I have Joi schema and want to add a custom validator for validating data which isn't possible with default Joi validators.

Currently, I'm using the version 16.1.7 of Joi

const method = (value, helpers) => {
  // for example if the username value is (something) then it will throw an 
  // error with the following message but it throws an error inside (value) 
  // object without error message. It should throw error inside the (error) 
  // object with a proper error message
        
  if (value === "something") {
    return new Error("something is not allowed as username");
  }

  return value; // Return the value unchanged
};
        
const createProfileSchema = Joi.object().keys({
  username: Joi.string()
    .required()
    .trim()
    .empty()
    .min(5)
    .max(20)
    .lowercase()
    .custom(method, "custom validation")
});
        
const { error, value } = createProfileSchema.validate({
  username: "something" 
});
        
console.log(value); // returns {username: Error}
console.log(error); // returns undefined

But I couldn't implement it the right way. I read Joi documents but it seems a little bit confusing to me. Can anyone help me to figure it out?

Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
Shifut Hossain
  • 1,599
  • 3
  • 14
  • 24

3 Answers3

40
const Joi = require('@hapi/joi');
    
Joi.object({
  password: Joi
    .string()
    .custom((value, helper) => {
      if (value.length < 8) {
        return helper.message("Password must be at least 8 characters long");
      }

      return true;
    })
}).validate({
    password: '1234'
});
Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
Muslem Omar
  • 1,021
  • 12
  • 12
34

Your custom method must be like this:

const method = (value, helpers) => {
  // for example if the username value is (something) then it will throw an 
  // error with the following message but it throws an error inside (value) 
  // object without error message. It should throw error inside the (error) 
  // object with a proper error message

  if (value === "something") {
    return helpers.error("any.invalid");
  }

  return value; // Return the value unchanged
};

Docs:

https://github.com/hapijs/joi/blob/master/API.md#anycustommethod-description

Output for value :

{ username: 'something' }

Output for error:

[Error [ValidationError]: "username" contains an invalid value] {
  _original: { username: 'something' },
  details: [
    {
      message: '"username" contains an invalid value',
      path: [Array],
      type: 'any.invalid',
      context: [Object]
    }
  ]
}
Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
SuleymanSah
  • 17,153
  • 5
  • 33
  • 54
2

This is how I validated my code, have a look at it and try to format yours

const busInput = (req) => {
  const schema = Joi.object().keys({
    routId: Joi.number().integer().required().min(1)
      .max(150),
    bus_plate: Joi.string().required().min(5),
    currentLocation: Joi.string().required().custom((value, helper) => {
      const coordinates = req.body.currentLocation.split(',');
      const lat = coordinates[0].trim();
      const long = coordinates[1].trim();
      const valRegex = /-?\d/;
      if (!valRegex.test(lat)) {
        return helper.message('Laltitude must be numbers');
      }
      if (!valRegex.test(long)) {
        return helper.message('Longitude must be numbers');
      }
    }),
    bus_status: Joi.string().required().valid('active', 'inactive'),
  });
  return schema.validate(req.body);
};
Isaac
  • 37
  • 3
  • 6
    While this might answer the question, if possible you should [edit] your answer to include an explanation of *how* this code block answers the question. This helps to provide context and makes your answer much more useful to future readers. – Hoppeduppeanut Oct 06 '20 at 22:05
  • First argument of the `message()` is `LanguageMessages` which is `Record`. – Slava Fomin II Nov 29 '21 at 21:57
  • you could validate currentLocation as an array of numbers – alex Jun 02 '22 at 18:15