Валидация в Express: проверка входящих данных
Содержание
О проверке кода. Примеры не запускались в этой сессии (по договорённости — фокус на контенте, а не прогонах). Говорим прямо, а не ставим бейдж «проверено».
Любые данные, приходящие от клиента, — потенциально мусор или атака. Валидация в Express — первый барьер, отсекающий неверный ввод до того, как он навредит. Разберём, как это делать правильно.
Главное правило: не доверяй клиенту
Серверная валидация не заменяется клиентской — это мы уже видели на формах, и на сервере это критично.
// клиент может прислать ЧТО УГОДНО, минуя браузер:
// curl -X POST /users -d '{"age": "не число", "email": "мусор"}'
Клиентскую проверку тривиально обойти: отключить JavaScript, отправить запрос напрямую через curl или Postman, подделать данные. Поэтому сервер обязан проверять всё входящее сам, независимо от того, что там делает клиент. Правило абсолютное: клиентская валидация — для удобства пользователя, серверная — для безопасности, и она обязательна.
Проверка по схеме
Ручные if-проверки быстро превращаются в кашу. Современный подход — описать схему ожидаемых данных:
const { z } = require('zod');
// схема: как должны выглядеть данные
const userSchema = z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
age: z.number().int().min(18).max(120),
});
app.post('/users', (req, res) => {
const result = userSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.issues }); // 400 + детали
}
const user = result.data; // здесь данные ГАРАНТИРОВАННО валидны
// ...создаём пользователя
});
Схема декларативно описывает, какими должны быть данные: типы, длины, форматы, диапазоны. Библиотека проверяет req.body против неё и возвращает либо чистые данные, либо список ошибок. Это несравнимо надёжнее ручных проверок и служит заодно документацией к API.
- 1 Запрос с даннымиreq.body от клиента — доверия ноль
- 2 Проверка по схемеТипы, форматы, диапазоны, обязательность
- 3 Не прошло → 400Стоп, список ошибок по полям клиенту
- 4 Прошло → обработчикДанные гарантированно валидны, работаем
Валидация как middleware
Чтобы не дублировать проверку в каждом маршруте, её оформляют middleware:
// универсальное middleware валидации по схеме
function validate(schema) {
return (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.issues });
}
req.body = result.data; // подменяем на очищенные данные
next(); // всё ок — пропускаем к обработчику
};
}
// применяем к маршруту
app.post('/users', validate(userSchema), (req, res) => {
// req.body уже проверен и чист
});
Middleware валидации проверяет запрос до обработчика и либо отклоняет его с 400, либо пропускает с гарантированно чистыми данными. Обработчик маршрута больше не думает о проверке — он работает с данными, которым можно доверять. Одна функция validate(schema) переиспользуется на всех маршрутах.
Библиотеки
Zod— популярен, особенно в TypeScript-проектах: типобезопасные схемы, автоматический вывод типов, читаемый синтаксис. Частый выбор для новых проектов;Joi— зрелая библиотека с богатым набором правил, давно в экосистеме;express-validator— тесно интегрирован с Express, проверки навешиваются как цепочки middleware.
Все решают одну задачу — декларативную проверку входа. Правило прежнее: инструмент под задачу, для большинства новых проектов удобен Zod.
Ответ об ошибках
// структурированный ответ — клиент покажет ошибки у полей
res.status(400).json({
errors: [
{ field: 'email', message: 'неверный формат' },
{ field: 'age', message: 'должно быть не меньше 18' },
],
});
При ошибке возвращают 400 Bad Request и структурированный список ошибок по полям — какое поле не прошло и почему. Это позволяет клиенту показать сообщения рядом с конкретными полями формы, а не выдать одно общее «ошибка». Единый формат ошибок валидации по всему API упрощает жизнь клиентам.
Валидация — не вся безопасность
Важно не переоценить валидацию: она проверяет форму данных, но не защищает от всего:
- SQL-инъекции — от них спасают параметризованные запросы, а не валидация: никогда не склеивайте SQL строками с данными пользователя;
- XSS — защищает экранирование при выводе (
textContent, неinnerHTML), а не проверка на входе; - авторизация — «может ли этот пользователь это делать» — отдельный слой, не валидация.
Валидация — первый барьер (правильная форма данных), но безопасность многослойна. Считать, что «провалидировал — значит защитил», — опасное заблуждение.
Практический чек-лист
- валидируйте всё входящее на сервере, не доверяя клиенту;
- используйте схемы (Zod/Joi), а не россыпь
if; - оформляйте проверку как переиспользуемое middleware;
- при ошибке — 400 и структурированный список по полям;
- помните: валидация не заменяет параметризованные запросы и экранирование.
Смежные темы: валидация форм на клиенте — для удобства; middleware — где живёт проверка; REST API и коды; JWT-авторизация — отдельный слой. Полный список — в уроках Node.js.