Валидация в Joi строится вокруг декларативного описания схем, где каждый тип данных дополняется набором правил (rules), определяющих допустимые значения, преобразования и ограничения. Эти правила формируют цепочки методов, которые последовательно уточняют поведение схемы и конечный результат проверки.
Каждый тип данных в Joi представляет собой объект схемы, к которому
применяются методы-правила. Внутренне схема накапливает набор
ограничений, которые затем интерпретируются во время
validate().
const schema = Joi.string().min(3).max(30).required();
В данном случае применяются три правила:
Каждое правило не заменяет предыдущее, а расширяет набор ограничений.
Строковые схемы содержат наиболее богатый набор ограничений.
Joi.string().min(5).max(20)
Joi.string().length(10)
min(n) — минимальная длинаmax(n) — максимальная длинаlength(n) — строго фиксированная длинаJoi.string().pattern(/^[a-z]+$/)
Правило pattern задаёт регулярное выражение, которому
должна соответствовать строка.
Дополнительные специализированные правила:
Joi.string().email()
Joi.string().uri()
Joi.string().guid()
Эти правила реализуют часто используемые проверки форматов.
Joi.string().trim()
Joi.string().lowercase()
Joi.string().uppercase()
Эти правила не только валидируют, но и изменяют входное значение до
проверки или после неё (в зависимости от конфигурации
convert).
Joi.string().valid('admin', 'user', 'guest')
Joi.string().invalid('root')
valid() задаёт допустимые значенияinvalid() исключает конкретные значенияЧисловые схемы поддерживают математические ограничения и типизацию.
Joi.number().min(0).max(100)
min() — нижняя границаmax() — верхняя границаJoi.number().integer()
Гарантирует отсутствие дробной части.
Joi.number().positive()
Joi.number().negative()
Ограничивает знак значения.
Joi.number().multiple(5)
Значение должно быть кратным указанному числу.
Joi.number().precision(2)
Ограничивает количество знаков после запятой.
Joi.boolean().truthy('yes').falsy('no')
Булевы схемы могут расширять допустимые представления истины и лжи.
Joi.date().min('2020-01-01').max('2026-01-01')
Joi.date().iso()
Joi.date().timestamp()
iso() требует ISO-форматtimestamp() допускает Unix-времяМассивы используют структурные ограничения элементов.
Joi.array().min(1).max(10)
Joi.array().length(3)
Joi.array().items(Joi.string(), Joi.number())
Определяет допустимые типы элементов.
Joi.array().unique()
Исключает дублирование элементов.
Joi.array().ordered(
Joi.string(),
Joi.number()
)
Фиксирует позиционную структуру массива.
Joi.array().has(Joi.string().valid('admin'))
Объектные схемы задают структуру данных.
Joi.object({
name: Joi.string().required(),
age: Joi.number()
})
Joi.object().unknown(true)
Разрешает дополнительные ключи.
Joi.object().unknown(false)
Запрещает поля, не описанные в схеме.
Joi.object().with('password', 'username')
Joi.object().without('token', 'password')
with() — обязательное совместное присутствиеwithout() — взаимоисключениеJoi.object().xor('a', 'b')
Joi.object().or('a', 'b')
Joi.object().and('a', 'b')
xor — только одно полеor — хотя бы одноand — все одновременноУсловная логика реализуется через when.
Joi.object({
role: Joi.string(),
access: Joi.string().when('role', {
is: 'admin',
then: Joi.valid('full'),
otherwise: Joi.valid('limited')
})
})
Также поддерживаются альтернативные схемы:
Joi.alternatives().try(
Joi.string(),
Joi.number()
)
Базовый тип any содержит универсальные ограничения.
Joi.any().required()
Joi.any().optional()
Joi.any().forbidden()
Joi.any().default('value')
Joi.any().valid('a', 'b')
Joi.any().invalid('x')
Joi.any().allow(null)
Joi.any().strip()
Полностью удаляет поле из результата.
Глобальная настройка поведения обязательности:
Joi.object().presence('required')
Возможные режимы:
requiredoptionalforbiddenМеханизм расширения логики через custom.
Joi.string().custom((value, helpers) => {
if (value === 'bad') {
return helpers.error('string.invalid');
}
return value;
})
Позволяет реализовать произвольные проверки, не ограниченные встроенными правилами.
Joi поддерживает создание новых типов через extend.
const customJoi = Joi.extend((joi) => ({
type: 'positiveInt',
base: joi.number().integer().min(1),
rules: {
even: {
validate(value, helpers) {
if (value % 2 !== 0) {
return helpers.error('number.even');
}
return value;
}
}
}
}));
Поведение правил изменяется через параметры
validate.
Joi.validate(data, schema, {
abortEarly: false,
convert: true,
allowUnknown: true,
stripUnknown: true
});
abortEarly — остановка при первой ошибкеconvert — автоматическое приведение типовallowUnknown — разрешение лишних полейstripUnknown — удаление неизвестных полейpresence — глобальная обязательностьКаждое правило может сопровождаться персонализированными сообщениями:
Joi.string().min(5).messages({
'string.min': 'Слишком короткая строка'
})
Ошибки привязываются к конкретным кодам правил.
Правила Joi всегда комбинируются цепочками. Порядок влияет на читаемость, но не на логику валидации, поскольку схема интерпретируется как набор ограничений.
Joi.string()
.trim()
.min(3)
.max(10)
.pattern(/^[a-z]+$/)
.required()
Каждый вызов добавляет новое ограничение, формируя итоговую модель данных.
Комбинации правил позволяют строить сложные схемы:
whenobject().keys()valid/invalidstripdefault и
преобразования строкЭта система делает Joi не просто инструментом проверки типов, а декларативным механизмом описания контрактов данных.