Does Joi have the concept of "not"?

Viewed 484

The Question

I have an object that has been parsed from a JSON string. I want to confirm that its fields contain only primitive types (e.g. number, boolean, string). Specifically, I would like to confirm that all of the field values are not an object.

Does Joi have the concept of not? Can I do this without listing every possible non-object type?

The Attempt

I attempted to use any.disallow which is almost what I need (and, I realized after posting my question, is listed as having the alias not).

import Joi from 'joi'

const schema = Joi.object().keys({ 
  myKey: Joi.any().not(Joi.object()),
})

However this seems to take values. This means it doesn't compare against schemas, and will consider any value that isn't the literal value returned by Joi.object() as valid.

3 Answers

It seems the answer, for now, might be no.

That said, it is possible to define custom validation functions for logic that isn't baked into Joi.

Something like the following does work:

import Joi from 'joi'

const schema = Joi.object().keys({ 
  myKey: Joi.any().custom(
    (value, helpers) => {
      if (typeof value === 'object' && value !== null) {
        return helpers.error('any.invalid');
      }
      return value
    },
    'is not an object',
  ),
})

I found this which allows you to only accept primitives, but it's not the not you're looking for:

const example = { 2: 'str', 3: 4, 4: true, 5: { 3: 2 } }

Joi.object()
   .pattern(/^/, [Joi.boolean(), Joi.string(), Joi.number()])
   .validate(example)

Basically, /^/ is regex for accepting arbitrary keys and the array is syntatic sugar for Joi.alternatives.

Edit: I managed a schema that fails whenever the key is an object

Joi.object()
   .pattern(/^/, Joi.when(Joi.object(), { then: Joi.forbidden() }))
   .validate(example)

I almost got there with

Joi.object()
   .pattern(/^/, Joi.not(Object()))
   .validate(example)

But it only works with empty object such as {}, which I suspect it has to do with Joi.not only accepting literals.

I don't think there's a "not" option.

Here's another alternative combining .when and .forbidden:

const schema = Joi.object().keys({ 
  myKey: Joi.any(),
}).when(Joi.object({ myKey: Joi.object() }), {
    then: Joi.object({ myKey: Joi.forbidden() })
});
Related