JS: REST API (Fastify)
Теория: Валидация бизнес-правил
Встроенная валидация в Fastify отвечает за проверку структуры входных и выходных данных, но это только часть воронки валидации. Правильная структура не гарантирует, что данные пройдут следующий этап - проверку бизнес-требований.
Классический пример это валидация email. Если такой адрес уже есть, то мы не можем зарегистрировать нового пользователя с тем же адресом. Сюда же можно отнести требования к паролю, указание связанных данных (через идентификаторы) и другие правила. Часто они завязаны на согласованность данных в базе.
Подобную валидацию можно реализовать двумя способами:
- Самостоятельно делая выборки из базы данных, выполняя проверки и генерируя ответ.
- Используя готовую библиотеку.
Fastify не предоставляет для этого никаких встроенных средств, поэтому нужна готовая библиотека. Наиболее подходящей библиотекой для этой задачи будет VineJS.
VineJS
VineJS — это валидатор данных, ориентированный на обработку входных данных, поступающих по HTTP-запросам, таких как формы или JSON, с акцентом на серверные приложения. В отличие от библиотек типа Yup или Zod, которые чаще применяются для клиентской валидации, VineJS предоставляет более мощные возможности для асинхронных проверок и интеграции с базами данных или внешними API. Кроме валидации, VineJS поддерживает нормализацию данных. Например, можно автоматически привести строки к нижнему регистру, чтобы обеспечить единообразие данных.
Установка библиотеки
Базовый пример использования
Работа Vine состоит из трех этапов:
- Описание схемы данных включая правила валидации и нормализации. Помимо стандартных правил для подобных библиотек, VineJS содержит множество правил специфичных для HTTP. Например, в примере выше, метод
confirm()проверяет, что передано полеpassword_confirmationсо значением совпадающим с исходным полемpassword. - Компиляция схемы для ускорения работы. Выполняется ровно один раз на всем протяжении жизни приложения.
- Проверка и преобразование данных. Дальше по коду используется объект, вернувшийся после вызова метода
validate(), так как внутри могут происходить преобразования типов и нормализация.
Интеграция VineJS в проект
Мы уже используем встроенную валидацию в Fastify, поэтому часть связанную с проверкой обязательности и преобразования типов в VineJS можно пропустить. По умолчанию VineJS пропускает и возвращает только те поля, которые указаны в схеме. Это поведение меняется вызовом метода allowUnknownProperties():
Благодаря такому подходу, нам останется описать только те правила, которые невозможно выразить в JSON Schema.
Подключение
-
Создайте директорию validators в корне проекта.
-
Добавьте в нее файл UserValidator.js с таким содержимым:
Вариант с классом опциональный. Существует множество разных способов, как можно описать валидаторы в приложении. Главное правило - схема должна компилироваться один раз.
Использование
На текущий момент мы получили 3 уровня проверки данных:
- Проверка данных по OpenAPI спецификации самим Fastify.
- Проверка бизнес-правил через VineJS.
- Ограничения в базе данных
Проверка уникальности
В некоторых ситуациях стандартных проверок VineJS недостаточно, например, для проверки уникальности. Эта проверка завязана на работу с базой данных, поэтому ее придется написать самостоятельно. Для этого в VineJS есть механизм создания и использования кастомных правил. Для Drizzle файл с подобным правилом может быть реализован так:
Большая часть описания правила взята из офциальной документации VineJS. Его основной код находится там, где идет работа с базой данных:
Запрос проверяет наличие в базе записей с field.name равным value. Теперь посмотрим на использование этого правила:
Параметры метода uniqueRule попадают в наш правило в виде объекта options. Схему логично передавать именно тут, потому что в проекте она определяется статично, в отличие от соединения с базой данных, которое создается во время работы приложения. Поэтому UserValidator принимает такой вид:
Внутри правила метаданные доступны в объекте field.meta.
Обработка ошибок
Когда валидация заканчивается с ошибкой, то выбрасывается исключение, которое Fastify никак не обрабатывает. Поэтому, по умолчанию, Fastify вернет 500 без объяснения, что произошло. Это поведение нужно менять, причем делать это глобально для всего приложения. Для этого в файл app.js нужно добавить такой обработчик:
Здесь мы проверяем тип ошибки и если это ошибка VineJS, то мы формируем объект с описанием ошибок и кодом 422. Почему именно такая структура? В HTTP существует стандарт Problem Details for HTTP APIs (RFC 9457), который описывает как нужно возвращать ошибки. С ним мы разберемся в следующем уроке.
Рекомендуемые программы
Завершено
0 / 15
