Валидация на стороне сервера

Валидация входящих данных на сервере с использованием Yup строится вокруг схем, описывающих структуру и ограничения объектов. Каждая схема определяет набор правил, по которым проверяются данные, поступающие из внешних источников — HTTP-запросов, очередей сообщений или интеграционных сервисов. Основной принцип заключается в том, что структура данных описывается декларативно, после чего Yup выполняет проверку на соответствие без необходимости ручной реализации условий.

Схема в Yup представляет собой объект, созданный на основе базовых типов: строк, чисел, булевых значений, массивов и объектов. Каждый тип обладает набором методов для уточнения правил проверки.

Пример базовой схемы:

import * as yup from 'yup';

const userSchema = yup.object({
  email: yup.string().email().required(),
  password: yup.string().min(8).required(),
  age: yup.number().min(18).required()
});

Такая структура позволяет описывать требования к данным без привязки к конкретному фреймворку. На сервере это особенно важно, поскольку одна и та же схема может использоваться в разных слоях приложения.

Асинхронная природа проверки

Yup поддерживает асинхронную валидацию, что критично для серверной среды. Это позволяет подключать проверки, зависящие от внешних источников: базы данных, API или кэш-систем.

const schema = yup.object({
  username: yup.string().test(
    'check-unique',
    'Username already exists',
    async (value) => {
      const user = await database.findUser(value);
      return !user;
    }
  )
});

Асинхронные тесты возвращают Promise, что делает возможным интеграцию с любыми I/O операциями. При обработке запросов это позволяет блокировать дальнейшую обработку до завершения проверки.

Использование Yup в серверных фреймворках

В Node.js-окружении Yup чаще всего интегрируется в middleware-слой. Например, в Express валидация может выполняться до передачи управления обработчику маршрута.

const validate = (schema) => async (req, res, next) => {
  try {
    req.body = await schema.validate(req.body, { abortEarly: false });
    next();
  } catch (err) {
    res.status(400).json({
      errors: err.errors
    });
  }
};

Данный подход позволяет централизовать проверку входных данных и исключить дублирование логики в контроллерах.

Стратегия обработки ошибок

Yup формирует структурированные ошибки, содержащие список всех нарушений. При использовании параметра abortEarly: false происходит полное сканирование объекта, что особенно полезно в серверной валидации, где важно предоставить полный список проблем.

try {
  await schema.validate(req.body, { abortEarly: false });
} catch (err) {
  console.log(err.inner);
}

Каждый элемент inner содержит путь к полю и описание нарушения. Это позволяет формировать стандартизированные ответы API.

Валидация вложенных объектов

Серверные данные часто имеют сложную структуру с вложенными объектами и массивами. Yup предоставляет инструменты для глубокой проверки таких структур.

const orderSchema = yup.object({
  user: yup.object({
    id: yup.string().required(),
    email: yup.string().email().required()
  }),
  items: yup.array().of(
    yup.object({
      productId: yup.string().required(),
      quantity: yup.number().min(1).required()
    })
  )
});

Такая модель позволяет гарантировать целостность данных на всех уровнях вложенности.

Кастомные правила валидации

Серверная логика часто требует бизнес-правил, которые невозможно выразить стандартными методами. Yup предоставляет механизм test, позволяющий реализовывать произвольные проверки.

const passwordSchema = yup.string().test(
  'strong-password',
  'Password is too weak',
  (value) => {
    const hasNumber = /\d/.test(value);
    const hasLetter = /[a-zA-Z]/.test(value);
    return hasNumber && hasLetter;
  }
);

Кастомные проверки могут использовать контекст, что позволяет передавать дополнительные параметры через .context().

Переиспользование схем и композиция

Одним из ключевых преимуществ Yup в серверной архитектуре является возможность композиции схем. Базовые структуры могут расширяться и комбинироваться без дублирования кода.

const baseUser = yup.object({
  email: yup.string().email().required()
});

const adminUser = baseUser.shape({
  role: yup.string().oneOf(['admin']).required()
});

Подход с наследованием схем упрощает поддержку сложных доменных моделей.

Синхронная и асинхронная валидация в API

В серверных приложениях важно различать синхронную и асинхронную валидацию. Синхронная проверка выполняется мгновенно и используется для простых ограничений. Асинхронная применяется при необходимости обращения к внешним системам.

await schema.validate(req.body); // может быть async

При высокой нагрузке асинхронные проверки могут влиять на производительность, поэтому часто применяется разделение на уровни: быстрая локальная проверка и отложенная бизнес-валидация.

Работа с частичными обновлениями данных

В API, поддерживающих PATCH-запросы, данные поступают частично. Yup позволяет адаптировать схемы под такие сценарии с помощью .noUnknown() и nullable().

const updateSchema = yup.object({
  email: yup.string().email(),
  age: yup.number().min(18)
}).noUnknown();

Отсутствие required() делает поля необязательными, что соответствует логике частичного обновления.

Интеграция с системой типов

В TypeScript-среде Yup может использоваться совместно с типизацией, обеспечивая согласованность между временем компиляции и выполнения. Хотя Yup не генерирует типы автоматически, схемы могут выступать источником истины для структуры данных.

type User = {
  email: string;
  password: string;
};

Схема при этом повторяет структуру типа, что снижает вероятность расхождения между логикой и контрактом данных.

Защита серверного слоя через валидацию

Валидация на сервере выполняет не только функцию проверки корректности, но и служит механизмом защиты от некорректных или вредоносных данных. Ограничение типов, диапазонов и форматов снижает поверхность атак и предотвращает попадание неконсистентных данных в бизнес-логику.

Особое значение имеет строгая проверка входных JSON-объектов, поскольку отсутствие структуры может приводить к непредсказуемому поведению системы.

Ограничения подхода

Несмотря на гибкость, Yup не предназначен для высокопроизводительных потоков валидации с миллионами операций в секунду. Глубокие схемы с большим количеством асинхронных тестов могут создавать нагрузку на event loop. В таких случаях применяются более легковесные решения или предварительная фильтрация данных до применения схем.

Также сложные условные зависимости между полями требуют аккуратного проектирования схем, поскольку чрезмерная вложенность ухудшает читаемость и поддержку.