При вызове schema.validate(value, options) библиотека
возвращает объект, содержащий как минимум два ключевых поля:
value и error. Именно работа с этими двумя
сущностями определяет весь дальнейший контроль над входными данными.
Типичный результат валидации выглядит следующим образом:
const result = schema.validate(data);
console.log(result);
{
value: { ... }, // преобразованные (или исходные) данные
error: null // либо объект ошибки, либо null
}
Если данные соответствуют схеме, поле error будет равно
null, а value может отличаться от исходного
объекта в зависимости от настроек преобразования.
value — это не просто входные данные. Joi может их
модифицировать:
"123" → 123)stripUnknown)Пример:
const schema = Joi.object({
age: Joi.number().default(18),
name: Joi.string().trim()
});
const { value } = schema.validate({ name: " Alex " });
Результат:
{
name: "Alex",
age: 18
}
Ключевой момент: value — это итоговая безопасная версия
данных, пригодная для дальнейшей работы.
Если валидация не прошла, error содержит объект типа
ValidationError.
const { error } = schema.validate({ age: "abc" });
Структура ошибки:
{
name: "ValidationError",
details: [
{
message: "\"age\" must be a number",
path: ["age"],
type: "number.base",
context: {
label: "age",
value: "abc",
key: "age"
}
}
]
}
details — это массив, содержащий все найденные нарушения
схемы. Каждый элемент описывает одну проблему.
Основные поля:
По умолчанию Joi прекращает проверку после первой ошибки:
const result = schema.validate(data, { abortEarly: true });
Чтобы получить полный список ошибок:
const result = schema.validate(data, { abortEarly: false });
Разница:
true → одна ошибкаfalse → массив всех ошибокПрактическое значение: режим false используется для
отображения форм с множественными подсказками.
Часто details не подходит напрямую для фронтенда. Обычно
требуется нормализация:
const formatErrors = (error) => {
return error.details.reduce((acc, item) => {
acc[item.path.join('.')] = item.message;
return acc;
}, {});
};
Результат:
{
age: "\"age\" must be a number"
}
Для вложенных объектов:
path: ["user", "email"]
Превращается в:
user.email
Joi позволяет кастомизировать сообщения:
const schema = Joi.object({
age: Joi.number().messages({
"number.base": "Возраст должен быть числом"
})
});
При ошибке:
{
message: "Возраст должен быть числом"
}
Это особенно важно для:
Синхронная валидация:
const { value, error } = schema.validate(data);
Асинхронная:
try {
const value = await schema.validateAsync(data);
} catch (error) {
console.log(error.details);
}
Особенности validateAsync:
async/await цепочекПри использовании validateAsync обработка строго через
исключения:
try {
const user = await schema.validateAsync(req.body);
} catch (err) {
return res.status(400).json({
errors: err.details
});
}
Важно учитывать:
err — это не строкаerr.details всегда массивТиповой шаблон:
const validate = (schema) => (req, res, next) => {
const { error, value } = schema.validate(req.body, {
abortEarly: false
});
if (error) {
return res.status(400).json({
errors: error.details
});
}
req.body = value;
next();
};
Ключевая идея:
value заменяет входные данныеerror останавливает выполнение запросаОпция stripUnknown изменяет итоговый value,
удаляя лишние поля:
const schema = Joi.object({
name: Joi.string()
});
const { value } = schema.validate(
{ name: "Alex", role: "admin" },
{ stripUnknown: true }
);
Результат:
{ name: "Alex" }
Это влияет не на error, а именно на итоговый объект.
Поле type позволяет строить логику обработки:
if (error.details[0].type === "string.empty") {
// специальная обработка пустых строк
}
Примеры типов:
string.basenumber.minany.requiredobject.unknownЭто используется для:
На практике результат Joi часто приводят к единому формату:
{
success: boolean,
data: object | null,
errors: object | null
}
Пример:
const result = schema.validate(data, { abortEarly: false });
return {
success: !result.error,
data: result.value,
errors: result.error ? formatErrors(result.error) : null
};
Joi сохраняет точную навигацию по объекту через
path:
{
user: {
profile: {
email: "invalid"
}
}
}
Ошибка:
path: ["user", "profile", "email"]
Это позволяет:
При использовании Joi.alternatives() ошибка может
содержать агрегированную информацию:
detailsЭто требует дополнительной фильтрации перед выводом пользователю.
Типовой алгоритм:
validateerrordetailsvalue как единственный источник доверенных
данныхЭтот подход делает Joi не просто валидатором, а полноценным механизмом подготовки данных к дальнейшей обработке.