Using Joi, require one of two fields to be non empty
Asked Answered
F

6

31

If I have two fields, I'd just like to validate when at least one field is a non empty string, but fail when both fields are empty strings.

Something like this does not validate

var schema = Joi.object().keys({
    a: Joi.string(),
    b: Joi.string()
}).or('a', 'b');

When validating against

{a: 'aa', b: ''}

The or condition only tests for the presence of either key a or b, but does test whether the condition for a or b is true. Joi.string() will fail for empty strings.

Here is gist with some test cases to demonstrate

http://requirebin.com/?gist=84c49d8b81025ce68cfb

Frigorific answered 4/3, 2015 at 20:59 Comment(0)
E
32

Code below worked for me. I used alternatives because .or is really testing for the existence of keys and what you really wanted was an alternative where you would allow one key or the other to be empty.

var console = require("consoleit");
var Joi = require('joi');

var schema = Joi.alternatives().try(
  Joi.object().keys({
    a: Joi.string().allow(''),
    b: Joi.string()
    }),
  Joi.object().keys({
    a: Joi.string(),
    b: Joi.string().allow('')
    })
);

var tests = [
  // both empty - should fail
  {a: '', b: ''},
  // one not empty - should pass but is FAILING
  {a: 'aa', b: ''},
  // both not empty - should pass
  {a: 'aa', b: 'bb'},
  // one not empty, other key missing - should pass
  {a: 'aa'}
];

for(var i = 0; i < tests.length; i++) {
  console.log(i, Joi.validate(tests[i], schema)['error']);
}
Episiotomy answered 5/3, 2015 at 17:6 Comment(1)
This does work, but isn't really scalable - if you had two sets of these, you'd need four alternatives, if you have three set of these then you'd need eight. It doesn't appear that there's a better way though.Magazine
C
13

An alternative way of using Joi.when() that worked for me:

var schema = Joi.object().keys({
  a: Joi.string().allow(''),
  b: Joi.when('a', { is: '', then: Joi.string(), otherwise: Joi.string().allow('') })
}).or('a', 'b')

.or('a', 'b') prevents a AND b being null (as opposed to '').

Corsetti answered 14/4, 2020 at 16:0 Comment(2)
I have just realised that this will return the message "b" is required rather than either "a" or "b" is required but don't know how to fix that.Corsetti
I believe you could use .error to specify your own error message, such as "A value is required for one of the following: (list attributes here)." The problem with scalability seems to remain, however. I have a case where there are 13 attributes and as long as you have a value in one of them, it's valid. I think I'd have to go with "c: Joi.when('a', {is: '', then: Joi.when('b', {is: '', then: Joi.string(), otherwise: Joi.string().allow('')})})"Unconscious
F
2

If you want to express the dependency between 2 fields without having to repeat all other parts of the object, you could use when:

var schema = Joi.object().keys({
  a: Joi.string().allow(''),
  b: Joi.string().allow('').when('a', { is: '', then: Joi.string() })
}).or('a', 'b');
Forewing answered 20/3, 2020 at 16:19 Comment(0)
C
2

You could just use .xor('a', 'b') instead .or('a', 'b'):

var schema = Joi.object().keys({
    a: Joi.string(),
    b: Joi.string()
}).xor('a', 'b');

This allows to be or only a or only b but not both, and not none

if your object is:
{ a: 'non-empty' } or { b: 'non-empty' }
will pass

if your object is:
{ a: '', b: '' } or { a: 'non-empty', b: 'non-empty' } 
will NOT pass

if your object is:
{ a: ''} or { b: '' } 
will NOT pass

if your object is:
{ a: '', b: 'non-empty' } or { a: 'non-empty', b: '' } 
will NOT pass

if your object is:
{ }
will NOT pass
Collectivism answered 14/4, 2023 at 21:13 Comment(2)
Would this support two arrays instead of two strings? For instance I want to prevent two empty arraysShiprigged
@Shiprigged I guess.Severance
H
0

the problem I faced with https://mcmap.net/q/463191/-using-joi-require-one-of-two-fields-to-be-non-empty answer is that if both fields are given you won't face any errors, if you want to fix that you need to replace or with xor.

Herwick answered 21/10, 2021 at 18:11 Comment(0)
V
0

&. Simply u can use '.and()'

  • it will not pass if either one of the fields is empty
var schema = Joi.object().keys({
   a: Joi.string(),
   b: Joi.string()
}).and('a', 'b');````
Viniferous answered 22/7, 2022 at 8:59 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Riven

© 2022 - 2024 — McMap. All rights reserved.