Валидация данных часто выходит за рамки проверки одного отдельного
значения. Во многих случаях необходимо учитывать взаимосвязь между
полями: сравнивать значения, проверять зависимости, создавать условные
ограничения. В библиотеке Joi для этого используется механизм ссылок
(ref), позволяющий обращаться к другим полям внутри объекта
схемы.
Joi.refJoi.ref() создаёт ссылку на другое поле в объекте,
которое может быть использовано в правилах валидации.
Простейший пример сравнения двух полей:
import Joi from 'joi';
const schema = Joi.object({
password: Joi.string().min(8).required(),
confirmPassword: Joi.string().valid(Joi.ref('password')).required()
});
В данном случае поле confirmPassword должно строго
совпадать со значением password. Если значения различаются,
валидация завершится ошибкой.
Ключевая особенность Joi.ref заключается в том, что это
не обычное значение, а динамическая ссылка, которая разрешается во время
выполнения валидации.
Ссылки поддерживают вложенные структуры данных. Для обращения к вложенным полям используется строковый путь с точечной нотацией:
const schema = Joi.object({
user: Joi.object({
password: Joi.string().required()
}),
confirmPassword: Joi.string().valid(Joi.ref('user.password')).required()
});
Здесь Joi.ref('user.password') указывает на вложенное
поле внутри объекта user.
Поддерживаются также массивы:
Joi.ref('items.0.name')
Такой путь обращается к первому элементу массива items и
его полю name.
ref в сравнительных операцияхJoi.ref часто применяется в сочетании с методами
сравнения:
const schema = Joi.object({
startDate: Joi.date().required(),
endDate: Joi.date().greater(Joi.ref('startDate')).required()
});
В этом примере endDate обязан быть больше
startDate, что типично для диапазонов дат.
Доступны и другие сравнения:
greater(Joi.ref(...))less(Joi.ref(...))min(Joi.ref(...))max(Joi.ref(...))Пример числовых ограничений:
const schema = Joi.object({
minValue: Joi.number().required(),
maxValue: Joi.number().min(Joi.ref('minValue'))
});
Ссылки могут использоваться не только внутри объекта, но и с доступом
к внешнему контексту валидации. Это позволяет подставлять значения из
context.
const schema = Joi.object({
price: Joi.number().max(Joi.ref('$maxPrice'))
});
При валидации:
schema.validate(
{ price: 100 },
{ context: { maxPrice: 150 } }
);
Значение $maxPrice подставляется из контекста. Префикс
$ указывает, что ссылка берётся не из объекта данных, а из
внешнего контекста.
refСсылки активно применяются в условной логике через
when.
const schema = Joi.object({
role: Joi.string().required(),
accessLevel: Joi.number().when('role', {
is: 'admin',
then: Joi.number().min(10),
otherwise: Joi.number().max(5)
})
});
Здесь логика зависит от значения другого поля, но можно использовать и ссылки:
const schema = Joi.object({
threshold: Joi.number().required(),
value: Joi.number().when(Joi.ref('threshold'), {
is: Joi.number().min(100),
then: Joi.number().min(50)
})
});
Такой подход позволяет строить динамические зависимости между полями.
Joi.ref поддерживает дополнительные параметры, влияющие
на поведение ссылки.
.valueJoi.ref('price', { adjust: value => value * 2 })
Функция adjust позволяет преобразовать значение перед
использованием.
Joi.ref('optionalField', { default: 0 })
Если поле отсутствует, будет использовано значение по умолчанию.
Валидация может использовать текущее значение через специальные конструкции:
Joi.valid(Joi.ref('.'))
Точка обозначает текущее значение в контексте проверки. Это используется реже, но полезно в сложных схемах трансформации.
ref работает внутри массивов и позволяет ссылаться на
элементы относительно текущей позиции:
Joi.array().items(
Joi.object({
min: Joi.number(),
max: Joi.number().min(Joi.ref('min'))
})
);
Каждый объект массива проверяется независимо, но ссылки остаются локальными для элемента.
Механизм работы Joi.ref включает несколько этапов:
Это означает, что значение ссылки всегда актуально на момент валидации, а не на момент объявления схемы.
Некорректное использование ref часто связано с:
user.passwordd)ref вне контекста объектаПример ошибки:
Joi.string().valid(Joi.ref('age'))
если age является числом, а поле строкой, сравнение
может быть некорректным.
В больших схемах ref позволяет создавать взаимосвязанные
структуры:
const schema = Joi.object({
minAge: Joi.number().required(),
maxAge: Joi.number().min(Joi.ref('minAge')),
user: Joi.object({
age: Joi.number().min(Joi.ref('..minAge')).max(Joi.ref('..maxAge'))
})
});
Двойная точка .. поднимается на уровень выше текущего
объекта, что позволяет строить ссылки через вложенность.
Если схема включает преобразования (.default(),
.cast(), .alter()), ссылки разрешаются после
применения этих операций, что важно учитывать при построении сложной
логики валидации.
Joi.number().default(10)
Если на это поле ссылаются другие поля, они будут использовать уже преобразованное значение.
ref в архитектуре
схемМеханизм ссылок является основой для построения зависимых моделей данных:
Без ref многие сценарии межполевой логики потребовали бы
ручной проверки вне схемы, что снижает декларативность валидации.