Joi: Add custom predicate functions to validate

Created on 8 Jul 2018  路  3Comments  路  Source: sideway/joi


I would like to see an option to add my own predicate functions to validate input with them, for example check a specific ISO Date in this specific format "yyyy-mm-dd hh:mm:ss"

Describe the problem you are trying to fix (provide as much context as possible)

I have my own predicate function that checks that kind of input from a request, I can handle it in the old fashion but I have seen that this package is doing the things right so I want to give it a try.

Which API (or modification of the current API) do you suggest to solve that problem ?

Add a new method like this: .predicate(fn, msg)

Are you ready to work on a pull request if your suggestion is accepted ?

Yes

feature

Most helpful comment

Hi guys! thank you for the feedback, I solved my doubts about how to do it.
@Marsup the problem with "joi-date-extensions" package is that it is only for Date Types Objects, in my case I cannot convert the input value to a Date object, the following is the format: "YYYY-MM-DD HH:MM:SS" allowed in the requirements, so if I convert it toISOString it will add +06:00 hours.

Anyway, these are the extensions that I made to solve it, if anybody wants to use it here it is, the idea is simple, it compares two strings, if a date is greaterThan other string in the schema it won't throw anything, otherwise it raises the errors:

/* joi_extensions.js */
export function datetime(joi) {
  return {
    name: 'string',
    base: joi.string(),
    language: {
      datetime: 'must be a valid format, (e.g.): YYYY-MM-DD HH:MM:SS'
    },
    rules: [{
      name: 'datetime',
      validate(params, value, state, options) {
        if (is.datetime(value)) {
          return value;
        } else {
          return this.createError('string.datetime', { value }, state, options);
        }
      }
    }]
  }
}

export function greaterThan(joi) {
  return {
    name: 'string',
    base: joi.string(),
    language: {
      greaterThan: 'must be greater than {{greaterThan}}'
    },
    rules: [{
      name: 'greaterThan',
      params: {
        greaterThan: joi.func().ref().required()
      },
      validate(params, value, state, options) {
        const prevName = params.greaterThan.root
        const prev = state.parent[prevName]
        const current = value
        if (current < prev) {
          return this.createError('string.greaterThan', { value, greaterThan: prevName }, state, options);
        }
        return value
      }
    }]
  }
}

Usage:

/* index.js */

import BaseJoi from 'joi'
import { datetime, greaterThan } from './joi_extensions'

const Joi = BaseJoi.extend(datetime).extend(greaterThan);

  /* ...skipped for brevity... */

const createdAt = Joi.string()
  .datetime();

const updatedAt = Joi.string()
  .datetime()
  .greaterThan(Joi.ref('created_at'));

const deletedAt = Joi.string()
  .datetime()
  .greaterThan(Joi.ref('created_at'))
  .greaterThan(Joi.ref('updated_at'));

const schema = Joi.object({
  /* ...skipped for brevity... */
  created_at: createdAt,
  updated_at: updatedAt,
  deleted_at: deletedAt
}).required().label('data')

const data = {
  /* ...skipped for brevity... */
  created_at: '2010-02-02 10:00:00',
  updated_at: '2010-02-02 15:00:00',
  deleted_at: '2010-02-02 04:00:00',
}

const result = schema.validate(data, { abortEarly: false })

console.log(result.error) // { ValidationError: child "deleted_at" fails because ["deleted_at" must be greater than created_at, "deleted_at" must be greater than updated_at]

By the way, I saw the package "joi-date-extensions" but I'd like to know if I could make the same things that I have achieved with my extensions, maybe only the part of compare a specific format.

It would be nice to add some extenstions like these to Joi or in "joi-date-extensions" package, what do you think?

Thanks.

All 3 comments

Have you tried your hand at writing an extension yet? They provide the ability to write your own custom validation functions.

If you have, what are you trying to do that an extension is not providing?

I think https://github.com/hapijs/joi-date-extensions already covers that one, but if not, an extension is your best bet.

Hi guys! thank you for the feedback, I solved my doubts about how to do it.
@Marsup the problem with "joi-date-extensions" package is that it is only for Date Types Objects, in my case I cannot convert the input value to a Date object, the following is the format: "YYYY-MM-DD HH:MM:SS" allowed in the requirements, so if I convert it toISOString it will add +06:00 hours.

Anyway, these are the extensions that I made to solve it, if anybody wants to use it here it is, the idea is simple, it compares two strings, if a date is greaterThan other string in the schema it won't throw anything, otherwise it raises the errors:

/* joi_extensions.js */
export function datetime(joi) {
  return {
    name: 'string',
    base: joi.string(),
    language: {
      datetime: 'must be a valid format, (e.g.): YYYY-MM-DD HH:MM:SS'
    },
    rules: [{
      name: 'datetime',
      validate(params, value, state, options) {
        if (is.datetime(value)) {
          return value;
        } else {
          return this.createError('string.datetime', { value }, state, options);
        }
      }
    }]
  }
}

export function greaterThan(joi) {
  return {
    name: 'string',
    base: joi.string(),
    language: {
      greaterThan: 'must be greater than {{greaterThan}}'
    },
    rules: [{
      name: 'greaterThan',
      params: {
        greaterThan: joi.func().ref().required()
      },
      validate(params, value, state, options) {
        const prevName = params.greaterThan.root
        const prev = state.parent[prevName]
        const current = value
        if (current < prev) {
          return this.createError('string.greaterThan', { value, greaterThan: prevName }, state, options);
        }
        return value
      }
    }]
  }
}

Usage:

/* index.js */

import BaseJoi from 'joi'
import { datetime, greaterThan } from './joi_extensions'

const Joi = BaseJoi.extend(datetime).extend(greaterThan);

  /* ...skipped for brevity... */

const createdAt = Joi.string()
  .datetime();

const updatedAt = Joi.string()
  .datetime()
  .greaterThan(Joi.ref('created_at'));

const deletedAt = Joi.string()
  .datetime()
  .greaterThan(Joi.ref('created_at'))
  .greaterThan(Joi.ref('updated_at'));

const schema = Joi.object({
  /* ...skipped for brevity... */
  created_at: createdAt,
  updated_at: updatedAt,
  deleted_at: deletedAt
}).required().label('data')

const data = {
  /* ...skipped for brevity... */
  created_at: '2010-02-02 10:00:00',
  updated_at: '2010-02-02 15:00:00',
  deleted_at: '2010-02-02 04:00:00',
}

const result = schema.validate(data, { abortEarly: false })

console.log(result.error) // { ValidationError: child "deleted_at" fails because ["deleted_at" must be greater than created_at, "deleted_at" must be greater than updated_at]

By the way, I saw the package "joi-date-extensions" but I'd like to know if I could make the same things that I have achieved with my extensions, maybe only the part of compare a specific format.

It would be nice to add some extenstions like these to Joi or in "joi-date-extensions" package, what do you think?

Thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JbIPS picture JbIPS  路  4Comments

n-sviridenko picture n-sviridenko  路  3Comments

builtbybrayne picture builtbybrayne  路  4Comments

ashrafkm picture ashrafkm  路  3Comments

a-c-m picture a-c-m  路  3Comments