Библиотека Validator.js изначально ориентирована на проверку строковых значений, однако в реальных приложениях данные редко ограничиваются простыми строками. Чаще всего приходится работать с объектами, массивами, вложенными структурами и комбинированными формами данных, поступающими из API, форм или внешних источников.
Эффективная обработка сложных структур в таких условиях строится не на расширении самой библиотеки, а на грамотной архитектуре валидационного слоя, где Validator.js используется как базовый набор примитивных проверок.
При работе с объектами каждый ключ рассматривается как отдельная сущность, требующая собственной проверки. Типичная структура данных пользователя может выглядеть следующим образом:
const user = {
name: "Ivan",
email: "ivan@mail.com",
age: 25,
address: {
city: "Almaty",
zip: "050000"
}
};
Validator.js предоставляет набор функций для проверки отдельных значений, например:
isEmailisLengthisIntisEmptyЛогика валидации объекта строится вручную:
import validator from "validator";
function validateUser(user) {
const errors = {};
if (!validator.isLength(user.name || "", { min: 2 })) {
errors.name = "Имя слишком короткое";
}
if (!validator.isEmail(user.email || "")) {
errors.email = "Некорректный email";
}
if (!validator.isInt(String(user.age), { min: 0, max: 120 })) {
errors.age = "Недопустимый возраст";
}
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
Такой подход превращает объект в набор независимых проверок, каждая из которых изолирована и предсказуема.
При наличии вложенных объектов валидация требует рекурсивного
подхода. Например, поле address нельзя проверять как единое
значение — его необходимо разложить на составляющие.
function validateAddress(address) {
const errors = {};
if (!validator.isLength(address.city || "", { min: 2 })) {
errors.city = "Некорректный город";
}
if (!validator.isPostalCode(address.zip || "", "RU")) {
errors.zip = "Некорректный индекс";
}
return errors;
}
Интеграция вложенной проверки в основную структуру:
function validateUser(user) {
const errors = {};
const addressErrors = validateAddress(user.address || {});
if (Object.keys(addressErrors).length > 0) {
errors.address = addressErrors;
}
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
Такая схема позволяет масштабировать валидацию на произвольную глубину вложенности без изменения базовой логики.
Массивы требуют итерационной проверки каждого элемента. Например, список email-адресов:
const data = {
emails: ["test@mail.com", "invalid-email", "user@site.com"]
};
Валидация массива:
function validateEmails(emails) {
const errors = [];
emails.forEach((email, index) => {
if (!validator.isEmail(email)) {
errors.push({
index,
message: "Некорректный email"
});
}
});
return errors;
}
Интеграция в объектную структуру:
function validateData(data) {
const errors = {};
const emailErrors = validateEmails(data.emails || []);
if (emailErrors.length) {
errors.emails = emailErrors;
}
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
При работе с массивами важно сохранять индекс ошибки, чтобы обеспечить точную локализацию проблемы в исходных данных.
Сложные структуры часто содержат неоднородные типы данных. Перед валидацией требуется приведение значений к ожидаемому виду.
function normalizeUser(user) {
return {
...user,
name: (user.name || "").trim(),
email: (user.email || "").toLowerCase(),
age: String(user.age)
};
}
Нормализация уменьшает количество ложных ошибок и делает поведение проверок предсказуемым.
В сложных структурах часть полей может быть опциональной или зависеть от других значений.
function validateUser(user) {
const errors = {};
if (user.type === "admin") {
if (!validator.isLength(user.adminCode || "", { min: 5 })) {
errors.adminCode = "Некорректный код администратора";
}
}
if (user.isActive && !validator.isEmail(user.email || "")) {
errors.email = "Email обязателен для активного пользователя";
}
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
Такая модель позволяет учитывать бизнес-логику при проверке данных, а не ограничиваться синтаксической валидацией.
При увеличении сложности структуры целесообразно выделять повторяющиеся проверки в отдельные функции:
const isValidName = (name) =>
validator.isLength(name || "", { min: 2, max: 50 });
const isValidAge = (age) =>
validator.isInt(String(age), { min: 0, max: 120 });
Использование композиции:
function validateProfile(profile) {
const errors = {};
if (!isValidName(profile.name)) {
errors.name = "Некорректное имя";
}
if (!isValidAge(profile.age)) {
errors.age = "Некорректный возраст";
}
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
Композиционный подход уменьшает связность и упрощает поддержку логики.
В случаях, когда структура данных заранее неизвестна, используется универсальный обход:
function validateObject(obj, schema) {
const errors = {};
Object.keys(schema).forEach((key) => {
const rule = schema[key];
const value = obj[key];
if (rule.required && validator.isEmpty(String(value || ""))) {
errors[key] = "Поле обязательно";
return;
}
if (rule.type === "email" && !validator.isEmail(String(value || ""))) {
errors[key] = "Некорректный email";
}
if (rule.type === "int" && !validator.isInt(String(value || ""))) {
errors[key] = "Некорректное число";
}
});
return errors;
}
Пример схемы:
const schema = {
email: { required: true, type: "email" },
age: { required: true, type: "int" }
};
Такой подход приближает логику к примитивной схеме валидации без использования специализированных библиотек схем.
При работе с комплексными объектами результаты часто агрегируются в единый формат:
function aggregateValidation(...results) {
const errors = {};
results.forEach((result) => {
Object.assign(errors, result);
});
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
Это позволяет разделять валидацию по модулям и собирать итоговый результат на уровне сервиса.
Сложные структуры часто содержат:
null и undefinedstring | number)Защитная проверка становится обязательной частью каждого валидатора:
const safeString = (value) => (value == null ? "" : String(value));
Использование безопасных преобразований снижает вероятность ошибок выполнения и делает систему устойчивой к некорректным входным данным.
Валидация сложных структур перестаёт быть чисто технической задачей и становится частью бизнес-правил. Проверки начинают учитывать:
Validator.js в такой архитектуре выступает как слой примитивных предикатов, поверх которых строится доменная логика валидации, организованная через функции, композиции и рекурсию.