В основе цепочек валидации лежит идея поэтапной проверки входного значения набором независимых правил. В контексте библиотеки Validator.js каждая функция проверки выполняет одну конкретную задачу: определяет, соответствует ли строка заданному критерию, например формату email, длине строки или числовому диапазону.
Поскольку Validator.js предоставляет набор чистых функций, цепочка валидации формируется не внутри самой библиотеки, а на уровне архитектуры приложения. Это приводит к необходимости построения композиционных механизмов, объединяющих несколько проверок в единый поток обработки данных.
Каждая функция из Validator.js возвращает логическое значение. Это позволяет выстраивать последовательность вызовов с ранним выходом при несоответствии:
import validator from 'validator';
function validateEmail(value) {
if (!validator.isString(value)) return false;
if (!validator.isEmail(value)) return false;
if (!validator.isLength(value, { max: 254 })) return false;
return true;
}
Такой подход представляет собой линейную цепочку, где каждая следующая проверка выполняется только при успешном прохождении предыдущей.
Для масштабирования логики валидации применяется обобщённая структура цепочки, в которой набор правил задаётся динамически:
import validator from 'validator';
function runChain(value, rules) {
for (const rule of rules) {
const result = rule(value);
if (result !== true) {
return result;
}
}
return true;
}
Каждая функция-правило инкапсулирует отдельную проверку Validator.js:
const emailRules = [
(v) => validator.isString(v) || 'не строка',
(v) => validator.isEmail(v) || 'некорректный email',
(v) => validator.isLength(v, { max: 254 }) || 'слишком длинный email'
];
Подобная структура позволяет отделить описание правил от механизма их исполнения.
Цепочки становятся более гибкими при разбиении логики на переиспользуемые блоки. Например, базовая проверка строки:
const isNonEmptyString = (v) =>
typeof v === 'string' && !validator.isEmpty(v.trim());
Она может использоваться как часть любой более сложной цепочки:
const passwordRules = [
(v) => isNonEmptyString(v) || 'пустое значение',
(v) => validator.isLength(v, { min: 8 }) || 'минимум 8 символов',
(v) => /[A-Z]/.test(v) || 'нет заглавной буквы',
(v) => /[0-9]/.test(v) || 'нет цифры'
];
В некоторых случаях логика валидации зависит от контекста. Это приводит к появлению условных ветвлений внутри цепочки:
const rules = [
(v, ctx) => ctx.required ? !validator.isEmpty(v) || 'обязательное поле' : true,
(v, ctx) => ctx.type === 'email' ? validator.isEmail(v) || 'email некорректен' : true
];
Контекст передаётся вместе с проверяемым значением, что позволяет адаптировать цепочку под различные сценарии без дублирования кода.
Цепочки становятся стабильнее при предварительной нормализации входных значений. Validator.js содержит утилиты для преобразования строк, которые часто используются до начала основной цепочки:
const normalized = validator.trim(validator.toString(input));
После нормализации уменьшается вероятность ложных ошибок, связанных с пробелами, типами данных или регистром символов.
Альтернативный подход к цепочкам — выполнение всех проверок с накоплением ошибок:
function validate(value, rules) {
const errors = [];
for (const rule of rules) {
const result = rule(value);
if (result !== true) {
errors.push(result);
}
}
return errors;
}
Такой механизм полезен в интерфейсах форм, где требуется отображение всех ошибок сразу, а не только первой обнаруженной.
Validator.js не ограничивает расширение логики, позволяя внедрять собственные проверки в общий поток:
const isStrongPassword = (v) =>
validator.isLength(v, { min: 10 }) &&
/[!@#$%^&*]/.test(v);
Эти функции становятся равноправными участниками цепочки наряду со встроенными методами.
Порядок расположения правил в цепочке влияет на производительность. Более дешёвые проверки размещаются в начале:
Раннее отсеивание некорректных данных снижает количество дорогих операций, особенно при массовой обработке форм или потоков данных.
Типовая форма регистрации может использовать многоуровневую цепочку:
const usernameRules = [
(v) => isNonEmptyString(v) || 'пустое имя',
(v) => validator.isLength(v, { min: 3, max: 20 }) || 'некорректная длина',
(v) => validator.isAlphanumeric(v) || 'только буквы и цифры'
];
const emailRules = [
(v) => isNonEmptyString(v) || 'пустое значение',
(v) => validator.isEmail(v) || 'неверный email'
];
Каждое поле обрабатывается независимо, но по одинаковому принципу композиции правил.
Цепочки часто встраиваются в слой сервисов или middleware. Это позволяет централизовать контроль данных до попадания в бизнес-логику:
function validateUser(data) {
return {
username: validate(data.username, usernameRules),
email: validate(data.email, emailRules)
};
}
Такой подход упрощает сопровождение и делает поведение системы предсказуемым при росте количества правил.
Одной из частых ошибок является чрезмерное усложнение правил внутри одной функции, что приводит к потере читаемости. Другой проблемой становится нарушение порядка проверок, когда дорогие операции выполняются до базовых проверок типа и длины.
Также встречается дублирование логики между цепочками, возникающее при отсутствии переиспользуемых валидаторных блоков.