Валидация в 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. 1 Запрос с даннымиreq.body от клиента — доверия ноль
  2. 2 Проверка по схемеТипы, форматы, диапазоны, обязательность
  3. 3 Не прошло → 400Стоп, список ошибок по полям клиенту
  4. 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.

Частые вопросы

Зачем нужна валидация на сервере, если есть на клиенте?
Клиентскую проверку легко обойти — отключить JavaScript или отправить запрос напрямую в обход браузера. Сервер не должен доверять никаким входящим данным. Клиентская валидация для удобства, серверная для безопасности, и она обязательна.
Как валидировать тело запроса в Express?
Описать схему ожидаемых данных через библиотеку вроде Zod или Joi и проверить ею req.body. Если данные не проходят, вернуть 400 с описанием ошибок. Удобно оформить проверку как middleware, общее для маршрутов.
Какую библиотеку валидации выбрать?
Zod популярен в проектах на TypeScript, даёт типобезопасные схемы и выводит типы автоматически. Joi — зрелая библиотека с богатыми правилами. express-validator интегрирован с Express. Для новых проектов чаще берут Zod.
Что возвращать при ошибке валидации?
Код 400 Bad Request и структурированный список ошибок по полям: какое поле не прошло и почему. Это позволяет клиенту показать пользователю понятные сообщения рядом с конкретными полями формы.
Валидация — это то же, что защита от инъекций?
Связано, но не одно и то же. Валидация проверяет форму и тип данных. От SQL-инъекций защищают параметризованные запросы, от XSS — экранирование при выводе. Валидация — первый барьер, но не единственный слой безопасности.