Валидация входящих API запросов является ключевым этапом обработки данных на сервере. Она обеспечивает контроль структуры, типов и допустимых значений параметров до их попадания в бизнес-логику приложения. При отсутствии строгой проверки входных данных возрастает риск неконсистентного состояния системы, ошибок выполнения и уязвимостей, связанных с инъекциями и некорректной интерпретацией данных.
При построении REST или GraphQL API валидация рассматривается как отдельный слой, располагающийся до контроллеров и сервисов. На этом уровне происходит фильтрация и нормализация входящих данных: тела запроса, query-параметров, path-параметров и заголовков.
HTTP-запрос в типичном серверном приложении включает несколько областей данных:
1. Тело запроса (body) Содержит основные данные, передаваемые клиентом. Используется в POST, PUT, PATCH запросах.
2. Query-параметры (query string) Передаются в URL и часто используются для фильтрации, сортировки и пагинации.
3. Path-параметры (params) Являются частью маршрута и обычно идентифицируют ресурсы.
4. Заголовки (headers) Содержат метаинформацию: токены авторизации, тип контента, язык и др.
Каждый из этих источников требует отдельной стратегии проверки, так как их семантика и допустимые значения различаются.
Существует несколько основных стратегий обработки входных данных:
Явная ручная валидация Проверка каждого поля с использованием условий. Подходит для небольших проектов, но плохо масштабируется.
Схемная валидация Использование описательных схем, в которых заранее задаются правила для структуры данных. Такой подход применяется в большинстве современных библиотек валидации.
Middleware-валидация Интеграция валидации в промежуточные обработчики HTTP-запросов, особенно в экосистеме Express.js.
Библиотека Validator.js предоставляет набор функций для проверки и санитаризации строковых значений. В отличие от полноценных схемных валидаторов, она ориентирована на атомарные проверки: email, URL, числовые строки, длина, регэксп-совпадения и другие примитивные правила.
В контексте API она используется как низкоуровневый инструмент, поверх которого часто строятся собственные схемы или комбинируется с middleware-слоем.
Пример типовых проверок:
В серверной архитектуре на базе Express.js Validator.js обычно применяется внутри middleware, который перехватывает запрос до передачи в контроллер.
Пример логики проверки тела запроса:
import validator fr om "validator";
function validateCreateUser(req, res, next) {
const { email, password, age } = req.body;
const errors = [];
if (!validator.isEmail(email || "")) {
errors.push("Некорректный email");
}
if (!validator.isLength(password || "", { min: 8 })) {
errors.push("Пароль слишком короткий");
}
if (!validator.isInt(String(age || ""), { min: 0 })) {
errors.push("Некорректный возраст");
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
next();
}
Такой подход разделяет ответственность: контроллер получает уже проверенные данные, что снижает сложность бизнес-логики.
Query-параметры часто используются для управления выборкой данных, и их некорректная обработка может приводить к логическим ошибкам.
Пример проверки query string:
function validateQuery(req, res, next) {
const { page, lim it } = req.query;
if (!validator.isInt(page || "1", { min: 1 })) {
return res.status(400).json({ error: "page должен быть целым числом >= 1" });
}
if (!validator.isInt(limit || "10", { min: 1, max: 100 })) {
return res.status(400).json({ error: "limit вне допустимого диапазона" });
}
next();
}
Path-параметры требуют строгой проверки идентификаторов ресурсов:
function validateParams(req, res, next) {
const { id } = req.params;
if (!validator.isUUID(id)) {
return res.status(400).json({ error: "Некорректный идентификатор" });
}
next();
}
Валидация часто дополняется санитаризацией — приведением данных к безопасному и предсказуемому виду.
Типовые операции:
Пример:
const cleanEmail = validator.normalizeEmail(email, {
gmail_remove_dots: false,
});
Санитаризация особенно важна при работе с пользовательским вводом, который впоследствии отображается в интерфейсе или сохраняется в базе данных.
Стандартных функций Validator.js недостаточно для сложных доменных ограничений. В таких случаях формируются кастомные проверки.
Пример:
function isStrongPassword(value) {
return (
validator.isLength(value, { min: 10 }) &&
/[A-Z]/.test(value) &&
/[0-9]/.test(value) &&
/[^A-Za-z0-9]/.test(value)
);
}
Подобные функции часто выносятся в отдельные модули и переиспользуются в разных сервисах.
Единообразная структура ошибок является важной частью API-дизайна. Обычно используется централизованный формат ответа:
return res.status(400).json({
status: "error",
errors: [
{ field: "email", message: "Некорректный формат" }
]
});
Такой формат упрощает обработку ошибок на клиентской стороне и обеспечивает предсказуемость поведения API.
Часть проверок требует обращения к внешним источникам данных: базе данных или сторонним сервисам.
Пример проверки уникальности email:
async function validateEmailUnique(req, res, next) {
const user = await User.findOne({ email: req.body.email });
if (user) {
return res.status(400).json({ error: "Email уже используется" });
}
next();
}
Validator.js в таких сценариях используется только для первичной проверки формата, а бизнес-ограничения реализуются отдельно.
Корректная валидация снижает риск следующих проблем:
Валидация не заменяет параметризацию запросов и безопасные методы доступа к данным, но является важным дополнительным слоем защиты.
В крупных системах валидация строится по слоям:
Такое разделение позволяет поддерживать консистентность данных независимо от точки входа в систему и уменьшает связность компонентов.