В библиотеке Joi работа с массивами строится вокруг схемы
Joi.array(), которая позволяет задавать строгие правила как
для самого массива, так и для каждого его элемента. Основная идея
заключается в том, что массив рассматривается как контейнер, внутри
которого каждый элемент проходит собственную валидацию по заранее
заданным правилам.
При создании схемы массива используется метод items(), в
который передаётся схема элементов:
const Joi = require('joi');
const schema = Joi.array().items(Joi.string());
В этом случае массив считается валидным только тогда, когда каждый его элемент является строкой. Любое несоответствие типу приводит к ошибке валидации.
Пример проверяемых данных:
schema.validate(['a', 'b', 'c']); // валидно
schema.validate(['a', 1, 'c']); // ошибка
Схема элементов может быть любой сложности, включая вложенные объекты:
const schema = Joi.array().items(
Joi.object({
id: Joi.number().required(),
name: Joi.string().required()
})
);
Каждый элемент массива в этом случае должен быть объектом с
обязательными полями id и name.
Для контроля размера массива применяются методы min() и
max():
const schema = Joi.array().min(2).max(5);
Такой массив должен содержать от двух до пяти элементов включительно. Нарушение границ вызывает ошибку валидации.
Также используется length(), когда требуется строго
фиксированное количество элементов:
const schema = Joi.array().length(3);
Метод unique() гарантирует отсутствие повторяющихся
значений:
const schema = Joi.array().items(Joi.number()).unique();
Массив [1, 2, 3] будет валиден, а [1, 2, 2]
— нет.
Для более сложных структур можно задавать правило уникальности по ключу:
const schema = Joi.array().items(
Joi.object({
id: Joi.number(),
email: Joi.string()
})
).unique('id');
В этом случае проверяется уникальность значений поля id
внутри объектов.
Метод has() задаёт правило, что хотя бы один элемент
массива должен соответствовать определённой схеме:
const schema = Joi.array().items(Joi.number()).has(Joi.number().greater(10));
Такой массив обязан содержать минимум одно число больше 10.
Дополнительная гибкость достигается через alternatives()
внутри items(), позволяя комбинировать несколько типов:
const schema = Joi.array().items(
Joi.alternatives().try(
Joi.string(),
Joi.number()
)
);
Здесь каждый элемент может быть либо строкой, либо числом.
Метод ordered() используется, когда важно не только
содержимое, но и позиция элементов:
const schema = Joi.array().ordered(
Joi.string(),
Joi.number(),
Joi.boolean()
);
Такой массив должен строго соответствовать порядку: сначала строка, затем число, затем булево значение.
Пример:
schema.validate(['text', 10, true]); // валидно
schema.validate([10, 'text', true]); // ошибка
По умолчанию Joi не запрещает дополнительные элементы, если они
соответствуют общей схеме items(). Однако поведение можно
контролировать через sparse() и строгие схемы объектов
внутри массива.
При использовании объектов часто применяется строгая схема:
const schema = Joi.array().items(
Joi.object({
id: Joi.number().required()
}).strict()
);
Это усиливает контроль над структурой каждого элемента.
Иногда требуется допустить одиночное значение вместо массива. Для
этого используется single():
const schema = Joi.array().items(Joi.string()).single();
В этом случае допустимы как массив ['a', 'b'], так и
одиночное значение 'a', которое автоматически
рассматривается как массив из одного элемента.
Joi поддерживает многомерные структуры:
const schema = Joi.array().items(
Joi.array().items(Joi.number())
);
Такой вариант описывает массив массивов чисел.
Валидация выполняется рекурсивно: каждый уровень структуры проверяется отдельно по своей схеме.
Для сложных случаев используется custom(), позволяющий
определить собственную логику проверки:
const schema = Joi.array().items(
Joi.number().custom((value, helpers) => {
if (value % 2 !== 0) {
return helpers.error('number.odd');
}
return value;
})
);
Здесь каждый элемент массива проверяется на чётность, и при нарушении возвращается ошибка.
На практике схемы массивов часто содержат сразу несколько ограничений:
const schema = Joi.array()
.items(Joi.number().integer())
.min(1)
.max(10)
.unique()
.has(Joi.number().positive());
Такое описание требует, чтобы массив:
Подобная композиция позволяет создавать строгие и выразительные правила для структурированных данных.
При работе с API часто встречается массив объектов:
const schema = Joi.array().items(
Joi.object({
userId: Joi.number().required(),
roles: Joi.array().items(Joi.string()).min(1)
})
);
В этом случае каждый элемент массива представляет пользователя, а внутри него дополнительно валидируется вложенный массив ролей. Joi обрабатывает такую структуру по уровням, обеспечивая независимую проверку каждого слоя данных.