Условная валидация в Joi строится вокруг метода when,
который позволяет изменять правила схемы в зависимости от значений
других полей объекта. Это ключевой механизм для описания зависимостей
между данными, когда структура и требования к одному полю определяются
состоянием другого.
Метод when применяется к конкретному полю схемы и
принимает условие, по которому выбирается одна из альтернативных веток
валидации.
const Joi = require('joi');
const schema = Joi.object({
role: Joi.string().valid('admin', 'user'),
accessLevel: Joi.when('role', {
is: 'admin',
then: Joi.number().min(5),
otherwise: Joi.number().min(1)
})
});
В данном примере поле accessLevel получает разные
ограничения в зависимости от значения role. Если роль
администратора, требования строже.
when:
is, then, otherwiseОсновные компоненты условной валидации:
password: Joi.string().min(8),
confirmPassword: Joi.when('password', {
is: Joi.exist(),
then: Joi.required(),
otherwise: Joi.optional()
})
Условие может быть не только точным значением, но и проверкой существования поля.
Joi.ref для ссылок на поляJoi.ref позволяет явно ссылаться на другие поля и
использовать их значения в условиях.
const schema = Joi.object({
password: Joi.string().min(8),
repeatPassword: Joi.any().valid(Joi.ref('password')).required()
});
Здесь значение repeatPassword должно совпадать с
password.
when поддерживает не только сравнение с конкретным
значением, но и диапазоны или типы схем.
age: Joi.number(),
guardianConsent: Joi.boolean().when('age', {
is: Joi.number().min(18),
then: Joi.forbidden(),
otherwise: Joi.required()
})
Поле становится запрещённым или обязательным в зависимости от возраста.
Можно проверять существование значения через Joi.exist()
или Joi.not().
email: Joi.string().email(),
phone: Joi.when('email', {
is: Joi.exist(),
then: Joi.optional(),
otherwise: Joi.required()
})
Если email указан, телефон становится необязательным.
alternativesДля более сложных сценариев используется
Joi.alternatives().conditional, позволяющий описывать
множественные ветки логики.
const schema = Joi.object({
paymentMethod: Joi.string().valid('card', 'cash'),
cardNumber: Joi.alternatives().conditional('paymentMethod', [
{
is: 'card',
then: Joi.string().creditCard().required(),
otherwise: Joi.forbidden()
}
])
});
Такая форма удобна при большом количестве условий.
when работает не только на верхнем уровне, но и внутри
вложенных структур.
const schema = Joi.object({
user: Joi.object({
type: Joi.string().valid('guest', 'registered'),
email: Joi.string().email().when('type', {
is: 'registered',
then: Joi.required(),
otherwise: Joi.forbidden()
})
})
});
Условие применяется относительно соседних полей внутри объекта.
Одно из самых частых применений — динамическое изменение обязательности поля.
const schema = Joi.object({
deliveryType: Joi.string().valid('courier', 'pickup'),
address: Joi.string().when('deliveryType', {
is: 'courier',
then: Joi.required(),
otherwise: Joi.optional()
})
});
Поле адреса требуется только при доставке курьером.
Условная логика применяется и к массивам, включая проверку элементов.
const schema = Joi.object({
items: Joi.array().items(
Joi.object({
type: Joi.string().valid('digital', 'physical'),
downloadLink: Joi.string().uri().when('type', {
is: 'digital',
then: Joi.required(),
otherwise: Joi.forbidden()
})
})
)
});
Каждый элемент массива может иметь собственную условную логику.
Joi позволяет задавать несколько условий через массив правил.
const schema = Joi.object({
country: Joi.string(),
age: Joi.number(),
drivingLicense: Joi.string().when('country', {
is: 'US',
then: Joi.when('age', {
is: Joi.number().min(16),
then: Joi.required(),
otherwise: Joi.forbidden()
}),
otherwise: Joi.optional()
})
});
Такая структура позволяет строить вложенную бизнес-логику.
when с массивомМожно задавать разные ветки условий для одного поля.
const schema = Joi.object({
status: Joi.string(),
comment: Joi.string().when('status', [
{
is: 'rejected',
then: Joi.required()
},
{
is: 'approved',
then: Joi.optional()
}
])
});
Каждое условие проверяется последовательно.
Иногда логика зависит сразу от нескольких значений.
const schema = Joi.object({
hasPassport: Joi.boolean(),
country: Joi.string(),
visaNumber: Joi.string().when('hasPassport', {
is: true,
then: Joi.when('country', {
is: 'other',
then: Joi.required()
})
})
});
Здесь условия комбинируются через вложенные when.
Подтверждение пароля:
confirm: Joi.string().valid(Joi.ref('password')).required()
Ролевой доступ:
permissions: Joi.array().when('role', {
is: 'admin',
then: Joi.required(),
otherwise: Joi.forbidden()
})
Опциональные поля в зависимости от выбора:
contactMethod: Joi.string().valid('email', 'phone'),
email: Joi.string().email().when('contactMethod', {
is: 'email',
then: Joi.required()
}),
phone: Joi.string().when('contactMethod', {
is: 'phone',
then: Joi.required()
})
При использовании условной валидации важно учитывать порядок вычисления схем. Joi строит дерево правил, и некорректные ссылки на поля могут приводить к неожиданным результатам.
Использование Joi.ref предпочтительно, когда требуется
строгая привязка к значению другого поля без повторного указания
имени.
Глубоко вложенные условия усложняют читаемость схемы, поэтому логически сложные конструкции часто выносятся в отдельные валидаторы или функции построения схемы.