Что используешь для валидации входящих данных в Express.js?

Ответ

Для валидации в Express.js я предпочитаю связку Joi для описания схем и express-validator для интеграции с middleware. Это дает баланс между мощностью валидации и чистотой кода.

Типичная реализация:

  1. Определение схемы с Joi: Создаю централизованные, переиспользуемые схемы валидации.

    // schemas/userSchemas.js
    const Joi = require('joi');
    
    const createUserSchema = Joi.object({
      email: Joi.string().email().required(),
      password: Joi.string().min(8).pattern(new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*d)')).required(),
      age: Joi.number().integer().min(18).max(120),
      preferences: Joi.object({
        newsletter: Joi.boolean().default(false)
      })
    });
  2. Middleware-обертка для Express: Создаю универсальный валидатор.

    // middleware/validate.js
    const validate = (schema) => (req, res, next) => {
      const { error, value } = schema.validate(req.body, { abortEarly: false }); // abortEarly: false чтобы получить все ошибки
    
      if (error) {
        const errorMessages = error.details.map(detail => detail.message);
        return res.status(400).json({ 
          error: 'Validation failed', 
          details: errorMessages 
        });
      }
    
      // Заменяем req.body на провалидированные и, возможно, кастомизированные данные (например, приведенные типы)
      req.body = value;
      next();
    };
  3. Использование в роуте:

    // routes/userRoutes.js
    const express = require('express');
    const router = express.Router();
    const { createUserSchema } = require('../schemas/userSchemas');
    const validate = require('../middleware/validate');
    
    router.post('/users', validate(createUserSchema), async (req, res) => {
      // req.body здесь уже гарантированно валиден
      const newUser = await User.create(req.body);
      res.status(201).json(newUser);
    });

Почему такая связка? Joi предоставляет невероятно богатый DSL для описания любых правил, включая кастомные валидации и преобразования типов. express-validator отлично подходит для простых проверок прямо в роуте, но для сложных, вложенных объектов и бизнес-правил Joi выглядит чище и поддерживаемее. Для валидации параметров запроса (req.query, req.params) я также использую Joi, создавая отдельные схемы.

Ответ 18+ 🔞

А, валидация в Экспрессе! Ну тут, блядь, история как с выбором тачки — можно на жигулях ездить, а можно сразу на чём-то с турбиной. Я вот, например, предпочитаю связку Joi для описания правил и express-validator для встраивания в мидлварь. Получается, ёпта, идеальный баланс: мощь валидации и при этом код не превращается в пиздопроебибну.

Как я это обычно делаю, смотри:

  1. Схемы на Joi: Создаю отдельные файлы со схемами, чтобы не городить овердохуища правил прямо в роутах. Чисто, переиспользуемо.

    // schemas/userSchemas.js
    const Joi = require('joi');
    
    const createUserSchema = Joi.object({
      email: Joi.string().email().required(),
      password: Joi.string().min(8).pattern(new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*d)')).required(),
      age: Joi.number().integer().min(18).max(120),
      preferences: Joi.object({
        newsletter: Joi.boolean().default(false)
      })
    });
  2. Универсальный валидатор-мидлварь: Делаю одну функцию, которая принимает схему и дальше сама всё ебёт-колотит. Главное — abortEarly: false, чтобы получить сразу все косяки, а не по одному, а то терпения ноль ебать.

    // middleware/validate.js
    const validate = (schema) => (req, res, next) => {
      const { error, value } = schema.validate(req.body, { abortEarly: false }); // abortEarly: false чтобы получить все ошибки
    
      if (error) {
        const errorMessages = error.details.map(detail => detail.message);
        return res.status(400).json({ 
          error: 'Validation failed', 
          details: errorMessages 
        });
      }
    
      // Заменяем req.body на провалидированные и, возможно, кастомизированные данные (например, приведенные типы)
      req.body = value;
      next();
    };
  3. Применение в роуте: И вот тут уже красота. Подключаешь мидлварь, и в хендлер попадает уже гарантированно чистый req.body. Никаких сюрпризов.

    // routes/userRoutes.js
    const express = require('express');
    const router = express.Router();
    const { createUserSchema } = require('../schemas/userSchemas');
    const validate = require('../middleware/validate');
    
    router.post('/users', validate(createUserSchema), async (req, res) => {
      // req.body здесь уже гарантированно валиден
      const newUser = await User.create(req.body);
      res.status(201).json(newUser);
    });

А почему именно так, спросишь? Ну, Joi — это просто песня, ебать мои старые костыли. Там можно описать вообще любую, даже самую хитрожопую логику валидации, с кастомными правилами и преобразованиями. express-validator хорош для простых проверок прямо в цепочке роута, но когда у тебя объекты вложенные, с кучей условий — Joi выглядит в разы чище и надёжнее. Для req.query и req.params я тоже Joi юзаю, просто отдельные схемы пишу. В общем, доверия к этому подходу — не ноль, а вполне себе овердохуища.