Библиотека Joi строит систему валидации вокруг строгого описания схем и детализированных сообщений об ошибках, которые формируются на основе результата проверки входных данных. Основной принцип заключается в том, что каждая ошибка содержит структурированную информацию о том, какое правило схемы было нарушено, где именно это произошло и какие данные вызвали проблему.
При провале проверки Joi возвращает объект
ValidationError, который является расширением стандартного
объекта Error. Он содержит несколько ключевых элементов,
определяющих диагностику:
Основные поля ValidationError:
name — всегда "ValidationError",
используется для идентификации типа ошибкиmessage — агрегированное текстовое описание всех
ошибокdetails — массив объектов, описывающих каждую
конкретную ошибкуannotate — вспомогательная функция для визуализации
пути ошибки в структуре данных_original — исходные данные, переданные на
валидациюisJoi — признак принадлежности к JoiПоле details содержит наиболее важную информацию для
обработки ошибок. Каждый элемент массива представляет собой отдельное
нарушение правила схемы.
Структура элемента details:
message — текст сообщения об ошибкеpath — путь к полю, где произошла ошибка (массив
ключей)type — тип нарушения (например,
string.min, number.base)context — контекст ошибки (ожидаемые значения, лимиты,
метаданные)Пример типичного объекта:
{
message: '"age" must be greater than or equal to 18',
path: ['age'],
type: 'number.min',
context: {
limit: 18,
value: 16,
label: 'age',
key: 'age'
}
}
Joi генерирует сообщения автоматически на основе типа валидатора и контекста ограничения.
Часто встречающиеся типы:
any.required — обязательное поле отсутствуетstring.base — значение не является строкойstring.min / string.max — нарушение длины
строкиnumber.min / number.max — нарушение
числовых границnumber.base — значение не является числомarray.min / array.max — нарушение длины
массиваobject.unknown — обнаружено запрещённое полеКаждый тип сообщения связан с конкретным валидатором внутри схемы.
По умолчанию Joi может прекращать проверку при первой ошибке. Это поведение контролируется параметром:
validate(data, { abortEarly: false })
При false формируется полный список ошибок, а не только
первая.
Эти параметры влияют не только на структуру данных, но и на потенциальные ошибки:
allowUnknown: false — запрещает поля, не описанные в
схемеstripUnknown: true — удаляет лишние поля без генерации
ошибкиJoi предоставляет механизм переопределения стандартных сообщений
через объект messages().
const schema = Joi.object({
age: Joi.number().min(18).messages({
'number.min': 'Возраст должен быть не менее 18 лет',
'number.base': 'Возраст должен быть числом'
})
});
Сообщения привязываются к конкретным типам ошибок, что позволяет точно управлять текстом для каждого случая.
Помимо локальной настройки, возможно задавать общие сообщения для всей схемы:
const schema = Joi.object({
username: Joi.string().required(),
age: Joi.number().required()
}).messages({
'any.required': 'Поле обязательно для заполнения'
});
Такая конфигурация применяется ко всем полям, где возникает соответствующий тип ошибки.
Joi использует label для формирования человекочитаемых
сообщений. Его можно изменить:
Joi.string().label('Имя пользователя')
После этого сообщения об ошибке будут содержать более понятное имя поля.
Метод annotate() позволяет получить визуальное
представление ошибки с указанием пути:
error.annotate()
Результат показывает структуру данных с подсветкой места ошибки, что особенно полезно при работе с вложенными объектами.
При работе с объектами и массивами Joi формирует путь ошибки через
массив path.
Пример:
{
user: {
profile: {
age: 16
}
}
}
Ошибка будет содержать:
path: ['user', 'profile', 'age']
Это позволяет точно определить источник проблемы в глубоко вложенных данных.
При использовании alternatives() возможны составные
ошибки, когда ни одна из ветвей не подходит.
Joi.alternatives().try(
Joi.string(),
Joi.number()
)
Если оба варианта не проходят, Joi формирует обобщённое сообщение с указанием всех попыток.
Поле context содержит динамические данные,
использованные при проверке:
limit — ограничение (например, минимальная длина)value — фактическое значениеkey — имя поляlabel — отображаемое имяpeers — для зависимых правил (valid(),
invalid())Контекст используется при формировании финального текста сообщения.
Joi применяет несколько уровней формирования ошибок:
string, number,
object)required)min, max,
pattern)custom)messages)Каждый этап может перезаписать или дополнить сообщение предыдущего.
Функция custom() позволяет вручную формировать
ошибки:
Joi.number().custom((value, helpers) => {
if (value < 0) {
return helpers.error('number.negative');
}
return value;
});
Определение текста затем выполняется через
messages():
messages({
'number.negative': 'Число не может быть отрицательным'
});
Joi выполняет преобразования типов (например, строка → число). При
неудаче возникает ошибка type casting.
Пример:
"abc"Joi.number()Результат:
number.baseПри множественных ошибках Joi агрегирует сообщения в одно:
message: '"age" must be greater than 18. "name" is required'
Порядок зависит от параметра abortEarly и внутренней
сортировки details.
В некоторых архитектурах Joi интегрируется через слой обёртки, который преобразует ошибки в унифицированный формат API:
Это позволяет отделить внутреннюю структуру Joi от внешнего интерфейса приложения.
Режим strict() отключает преобразование типов, что
увеличивает количество ошибок type.base.
Joi.number().strict()
В этом режиме строковые числа больше не приводятся к числам автоматически.
При валидации массивов ошибки могут относиться к конкретному индексу:
path: ['items', 2]
Это позволяет точно определить элемент, нарушивший правило.
Метод describe() не влияет на ошибки напрямую, но
используется для анализа схем, из которых могут генерироваться
документационные сообщения или UI-описания форм.
В реальных приложениях часто выполняется преобразование Joi ошибок в унифицированный формат:
Такая нормализация строится на основе details, поскольку
именно он содержит полную информацию о нарушениях.