В Joi управление сообщениями об ошибках построено на системе ключей,
привязанных к конкретным правилам валидации. Каждое стандартное
сообщение связано с определённым типом ошибки: несоответствие типа,
пустое значение, нарушение ограничения длины, диапазона и так далее.
Переопределение этих сообщений выполняется на уровне схемы, отдельных
правил или глобальных настроек через механизм messages.
Каждое правило в Joi генерирует ошибку с уникальным кодом. Этот код используется как ключ при замене текста сообщения.
Примеры ключей:
string.base — значение не является строкойstring.empty — строка пустаяstring.min — строка короче минимальной длиныstring.max — строка длиннее допустимого значенияany.required — обязательное поле отсутствуетnumber.base — значение не является числомnumber.min — число меньше допустимогоnumber.max — число больше допустимогоКаждый из этих ключей может быть переопределён независимо от остальных.
Основной механизм кастомизации сообщений — метод
messages(), применяемый к схеме.
import Joi from 'joi';
const schema = Joi.object({
username: Joi.string()
.min(3)
.max(30)
.required()
.messages({
'string.base': 'Имя пользователя должно быть строкой',
'string.empty': 'Имя пользователя не может быть пустым',
'string.min': 'Имя пользователя слишком короткое',
'string.max': 'Имя пользователя слишком длинное',
'any.required': 'Имя пользователя обязательно'
})
});
В этом случае каждое правило получает собственное текстовое представление ошибки. Приоритет сообщений определяется ключами: если совпало несколько условий, используется сообщение наиболее конкретного правила.
Joi позволяет задавать сообщения прямо внутри цепочки методов, ограничивая область действия кастомизации.
const schema = Joi.object({
age: Joi.number()
.min(18)
.max(65)
.messages({
'number.base': 'Возраст должен быть числом',
'number.min': 'Возраст не может быть меньше 18 лет',
'number.max': 'Возраст не может превышать 65 лет'
})
});
Такой подход делает логику сообщений локальной и привязанной к конкретному полю.
Joi поддерживает подстановку значений из контекста ошибки. Это позволяет формировать динамические сообщения с использованием параметров правила.
Доступные переменные зависят от типа правила:
{#limit} — ограничение (min/max){#value} — переданное значение{#label} — имя поляПример:
const schema = Joi.object({
password: Joi.string()
.min(8)
.messages({
'string.min': 'Пароль должен содержать не менее {#limit} символов'
})
});
В момент генерации ошибки {#limit} заменяется на
фактическое значение 8.
Когда требуется единый стиль сообщений для всей схемы, переопределение выполняется на уровне каждого поля или через композицию схем.
const baseStringMessages = {
'string.base': 'Значение должно быть строкой',
'string.empty': 'Значение не может быть пустым'
};
const schema = Joi.object({
firstName: Joi.string().messages(baseStringMessages),
lastName: Joi.string().messages(baseStringMessages),
city: Joi.string().messages(baseStringMessages)
});
Такой подход устраняет дублирование и обеспечивает единообразие сообщений.
Для масштабных приложений применяется настройка поведения валидатора
через prefs. Она позволяет задать общие сообщения, которые
применяются ко всем схемам.
const schema = Joi.object({
email: Joi.string().email().required(),
phone: Joi.string().required()
}).prefs({
messages: {
'string.base': 'Поле должно быть текстовым значением',
'any.required': 'Обязательное поле отсутствует'
}
});
При таком подходе локальные messages() могут
переопределять глобальные значения, если конфликтуют по ключу.
Система разрешения сообщений работает по уровневой модели:
messages() у поля)prefs({ messages })Более локальный уровень всегда перекрывает глобальный.
Одно правило может генерировать несколько разных ошибок, и каждая из них требует отдельного сообщения.
const schema = Joi.object({
code: Joi.string()
.length(5)
.messages({
'string.base': 'Код должен быть строкой',
'string.length': 'Код должен содержать ровно 5 символов',
'any.required': 'Код обязателен'
})
});
Здесь один валидатор string.length имеет собственный
ключ, отличающийся от min и max, поскольку
проверяет точное значение.
Для поддержки крупных проектов часто создаются централизованные словари сообщений.
const messages = {
required: {
'any.required': 'Поле обязательно для заполнения'
},
string: {
'string.base': 'Ожидается строка',
'string.empty': 'Строка не может быть пустой'
}
};
const schema = Joi.object({
login: Joi.string().messages({
...messages.string,
...messages.required
})
});
Такой подход позволяет стандартизировать текст ошибок без дублирования логики.
При работе с вложенными структурами каждое поле получает собственный контекст сообщений, и переопределение выполняется на уровне вложенности.
const schema = Joi.object({
user: Joi.object({
name: Joi.string().required().messages({
'string.base': 'Имя должно быть строкой',
'any.required': 'Имя пользователя обязательно'
})
})
});
Сообщения не наследуются автоматически от родительских объектов, если явно не заданы.
Joi поддерживает шаблонизацию, позволяющую формировать универсальные сообщения для разных типов правил.
const schema = Joi.object({
score: Joi.number()
.min(0)
.max(100)
.messages({
'number.min': 'Значение должно быть не меньше {#limit}',
'number.max': 'Значение должно быть не больше {#limit}'
})
});
Здесь один шаблон используется для разных ограничений, что снижает количество дублируемого кода.
Некоторые цепочки правил могут генерировать несколько потенциальных ошибок, но Joi возвращает только одну, наиболее приоритетную. В таких случаях важно правильно расставлять сообщения, чтобы они отражали реальную причину сбоя.
const schema = Joi.object({
age: Joi.number()
.integer()
.min(18)
.messages({
'number.base': 'Возраст должен быть числом',
'number.integer': 'Возраст должен быть целым числом',
'number.min': 'Минимальный возраст — {#limit}'
})
});
Каждое правило здесь отвечает за отдельный сценарий ошибки, и их сообщения не конфликтуют между собой.
При увеличении сложности схемы критическим становится единообразие
ключей сообщений. Использование одинаковых ключей
(string.base, any.required,
number.min) во всех частях схемы позволяет централизованно
управлять текстами и избегать расхождений в поведении валидатора.
Сообщения становятся не просто текстовой надстройкой, а частью структуры описания правил валидации, тесно связанной с логикой схемы Joi.