Покрытие валидационных правил в Joi определяется полнотой описания допустимых состояний данных и степенью детализации ограничений, накладываемых на каждое поле схемы. В основе лежит принцип декларативного описания инвариантов, при котором каждое правило формализует конкретный аспект допустимости значения.
Полное покрытие валидации формируется на уровне схемы и включает несколько взаимосвязанных слоёв:
Каждый слой расширяет пространство ограничений, формируя плотную сетку правил, минимизирующую возможность неконсистентных данных.
Строковые схемы формируют наиболее насыщенную область правил:
Joi.string()
.min(3)
.max(30)
.pattern(/^[a-zA-Z0-9]+$/)
Покрытие включает:
emptyКомбинация ограничений позволяет описывать не только структуру строки, но и её семантическую допустимость.
Числовая область покрывается через диапазоны и дискретные ограничения:
Joi.number()
.integer()
.min(0)
.max(100)
.precision(2)
Покрытие включает:
Особое значение имеет сочетание min/max с
precision, так как оно определяет допустимое пространство
значений с высокой плотностью ограничений.
Объектные схемы формируют основу комплексной валидации:
Joi.object({
id: Joi.number().required(),
name: Joi.string().required(),
profile: Joi.object({
age: Joi.number().min(0),
email: Joi.string().email()
})
})
Покрытие включает:
unknown,
fork, keys)unknown(false) или
stripUnknownГлубина вложенности определяет полноту структурного покрытия, где каждый уровень может иметь собственный набор правил.
Массивы требуют двойного уровня валидации: самого контейнера и его элементов.
Joi.array()
.items(Joi.string().min(2))
.min(1)
.max(10)
.unique()
Покрытие включает:
itemsВ случае сложных массивов возможно комбинирование схем:
Joi.array().items(
Joi.object({ type: Joi.string(), value: Joi.any() }),
Joi.string()
)
Это создаёт полиморфное покрытие, при котором массив допускает разные формы элементов.
Условная логика расширяет пространство допустимых данных:
Joi.object({
role: Joi.string().valid('admin', 'user'),
permissions: Joi.when('role', {
is: 'admin',
then: Joi.array().items(Joi.string()),
otherwise: Joi.forbidden()
})
})
Механизмы:
when для зависимых правилalternatives для альтернативных схемswitch для множественных условийУсловные конструкции создают динамическое покрытие, изменяющееся в зависимости от контекста данных.
Joi.alternatives().try(
Joi.string(),
Joi.number()
)
Покрытие альтернатив позволяет описывать неоднозначные входные данные, где допустимы разные типы или структуры.
Расширение базового набора правил позволяет формировать доменно-ориентированную валидацию:
const custom = Joi.extend((joi) => ({
type: 'evenNumber',
base: joi.number(),
validate(value, helpers) {
if (value % 2 !== 0) {
return { value, errors: helpers.error('number.even') }
}
}
}))
Такое расширение создаёт новые области покрытия, не ограниченные стандартной библиотекой.
Модель присутствия значений определяет, насколько строго схема покрывает отсутствие данных:
required() — обязательное полеoptional() — допускается отсутствиеforbidden() — запрещённое полеdefault() — значение по умолчаниюКомбинации этих правил определяют плотность покрытия в рамках схемы объекта.
Механизмы контроля избыточных данных влияют на фактическое покрытие:
Joi.object({
a: Joi.string()
}).unknown(false)
или
Joi.object({
a: Joi.string()
}).options({ stripUnknown: true })
Эти настройки определяют, входят ли посторонние поля в область допустимого состояния или исключаются из неё.
Преобразования влияют на финальное пространство валидных значений:
trim() изменяет допустимые строковые формыlowercase() и uppercase() нормализуют
регистрgreater, less, isoDate
формируют контекстные ограниченияФактическое покрытие включает не только входные значения, но и их трансформированные формы после обработки.
При глубокой вложенности объектов покрытие становится транзитивным: изменение в одном узле схемы влияет на допустимость целого поддерева данных.
Joi.object({
user: Joi.object({
profile: Joi.object({
settings: Joi.object({
theme: Joi.string()
})
})
})
})
Каждый уровень добавляет дополнительный слой ограничений, формируя каскадную модель проверки.
Схемы могут комбинироваться для расширения покрытия:
const base = Joi.object({ id: Joi.number() })
const extended = base.keys({
name: Joi.string()
})
Композиция позволяет переиспользовать существующие правила без потери полноты ограничений.
Различие между частичной и полной валидацией определяется уровнем обязательности правил:
Это особенно заметно при работе с API-данными, где входные структуры могут эволюционировать.
Покрытие правил считается неполным без учёта пограничных значений:
Обработка этих состояний требует явного включения правил, иначе пространство допустимых данных остаётся недоопределённым.