api-contract-check: валидаторы API из TS-типов, рантайм меньше 1 КБ

· ·

api-contract-check: валидаторы API из TS-типов, рантайм меньше 1 КБ

PrimeCoder · 2026 · формат: tech

Разбор новинки простыми словами: что это, кому нужно и брать ли.

Валидацию ответов бэкенда можно строить из уже написанных TS-типов, минуя тяжёлый OpenAPI-пайплайн — это снимает рутину перегенерации контрактов у фронтенд-команд.

Связанные услуги: каталог, AI Boost Team, стать клиентом.

Валидация, которая появляется поздно или никогда

Валидация ответов бэкенда редко планируется. Она появляется после первого инцидента: бэкенд обещал строку, прислал null, форма упала у клиента, поддержка получила три письма. До этого момента вызовы fetch живут в useEffect и никто их не проверяет.

В большинстве проектов, которые видел, валидация не производилась вообще — вызовы XHR / fetch выполнялись в componentDidMount / useEffect или аналогах для других фреймворков.

Автор библиотеки api-contract-check Дмитрий Казаков (DmitryKazakov833) выложил в open source инструмент, который закрывает эту дыру без отдельного проекта и без согласований с бэкендом. Логика: DTO-модели на TypeScript на фронте уже написаны, значит валидаторы можно собрать из них на этапе сборки. Не заново писать схемы, не ждать npm-пакет со спецификацией, не спорить в чате о том, чья версия контракта правильная.

Библиотека входит в набор @arfa-tools/generator, документация лежит на русском и английском. Материал свежий: на момент выгрузки сигнала статья провисела на Хабре около получаса, заявленное время прочтения — 7 минут, охват — 872 читателя. То есть это не отраслевая сенсация, а рабочий инструмент, который автор вынес из собственной практики.

Что именно вы получаете

Два модуля с разными задачами.

Сборочный. Функция createValidators принимает список файлов с TS-моделями, путь к tsconfig и имена типов, которые описывают запрос и ответ. На выходе — обычный JS-объект со схемой. Автор приводит пример: для файла getUser схема выглядит как { request: { id: 'string' }, response: { name: 'string' } }. Ничего магического, никакого бинарного формата.

Дальше с этим объектом делайте что хотите: сериализуйте через JSON.stringify и положите в файл, который подгружается лениво; конвертируйте в схему Zod или аналога; выводите в документацию; держите прямо в коде вызова. Схема — данные, а не запечатанный артефакт.

Рантайм. Модуль проверяет запрос и ответ по схеме, вычищает лишние поля и складывает их через getExtraneous в лог. Вес — меньше 1 КБ в сжатом виде. Для сравнения: любой популярный валидатор форм в бандле тянет на порядок больше, даже если вы используете три поля из его API.

Все лишнее будет очищено и залогировано, а необходимое — проверено на соответствие TS-типам.

Есть колбэк getDegraded. Он срабатывает, когда конструкцию TypeScript не удалось разобрать до конца и схема получилась ослабленной. Полезная штука: вместо тихой деградации вы видите список имён и решаете, чинить тип или принять риск. Автор отдельно отмечает собственный syntax interpreter для разбора TS AST — по его бенчмаркам он быстрее ts.createProgram, oxc-parser и SWC. Построение схемы для файла с графом из 100 импортов — около 2 мс. Цифра author-side, но порядок величин понятен: это не минуты на CI.

Откуда взялась проблема: короткая история трёх подходов

Сначала в энтерпрайзе придумали генерировать всё из OpenAPI. Бэкенд отдавал спецификацию, из неё рождались TS-типы или ready-made клиенты вида api.getUser. Звучит стройно. На практике всё ломается об организацию: спецификация расходится с реальными ответами (при param?: string прилетает null), версионирование пакетов превращается в rebase-марафон несколько раз в день, фронт не может начать параллельно с бэком, а монолитный клиент не поддаётся tree-shaking — на главной нужен один эндпоинт, в бандл едут все.

Потом появился ручной путь: Zod, Valibot, схемы пишут руками на фронте. Удобно, пока проект небольшой. Дальше выясняется, что типы в проекте живут в двух видах — написанные руками и выведенные через infer, — и это путает. Что схемы тяжелее, чем нужно для проверки контракта. Что typecheck от вывода типов ползёт медленнее.

Третий путь — генерация из TS-типов — не новый. Но существующие решения не затачивались под API: не чистят лишние поля, не логируют их, не всегда дают сериализуемый результат.

ПодходКто платитГлавный риск
Генерация из OpenAPIИнфраструктура и организационные согласованияСхема расходится с реальным ответом, клиент монолитный
Ручные схемы (Zod, Valibot)Время разработчика на каждое полеДва источника типов, размер бандла
Валидаторы из TS-типовОдин раз настроить сборкуНеполный разбор сложных конструкций TS

Чем это отличается от ts-interface-builder и typia

Похожие инструменты на рынке есть, автор сам их называет. ts-interface-builder генерирует отдельные файлы с валидаторами, typia правит код плагином бандлера. Разница в фокусе: api-contract-check собран вокруг конкретной задачи — проверить запрос и ответ на границе с бэкендом, вычистить лишнее, залогировать вычищенное, отдать сериализуемую схему.

Последний пункт недооценивают. Если схема — обычный JS-объект, её можно скормить другой библиотеке проверки или вывести в документацию без конвертеров и переходников. Это снимает главный страх при выборе валидатора: «а если через год захотим переехать». Переезд стоит один JSON.stringify.

Кому это нужно на самом деле

Не всем. Разберём по ситуациям.

  • Фронтенд-команда из 2–10 человек, бэкенд отдельный. Основной сценарий. Контракты меняются, спецификация либо устарела, либо её нет. Валидаторы из типов ставятся за один вечер, дальше работают молча.
  • Интеграция с внешними API. Когда чужая система присылает что хочет, а искать рассогласование приходится по логам через неделю после инцидента. Точка проверки на входе экономит дни.
  • Проект на своём бэкенде с одним фронтом. Можно, но выгода меньше: у вас и так один источник правды, а типы обычно шарятся пакетом.
  • Энтерпрайз с выстроенным OpenAPI-пайплайном. Менять работающее на слово «удобнее» не надо. Хотя бы потому, что миграция затронет и бэкенд-команду, и релизный процесс.

Практический шаг: возьмите один самый болезненный эндпоинт. Тот, который чаще ломался. Опишите тип запроса и ответа, прогоните через createValidators, посмотрите на схему глазами. Если схема совпала с ожиданиями и вылезли неочевидные поля — вы нашли деньги, которые теряли на отладке.

Где подвох

Инструмент молодой, и это меняет правила игры в оценке рисков. Бенчмарк на 2 мс — замер автора, независимой проверки нет. Охват 872 читателя на старте означает: широкой базы пользователей, которая выловит краевые случаи, пока нет.

Второй момент — парсер TS. Собственный syntax interpreter, который быстрее штатных, это одновременно преимущество и обязательство. Чем экзотичнее конструкция в ваших DTO (условные типы, mapped types, сложные дженерики), тем выше шанс попасть в getDegraded. Хорошо, что деградация видна и логируется. Плохо, что разбираться с ней вам, а не автору.

Третье: библиотека живёт внутри набора @arfa-tools/generator. Это не отдельный пакет с собственным жизненным циклом. Для небольшой команды нормально, для компании с требованиями к вендорской поддержке — повод подумать.

Вердикт

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

Экономика простая. Настройка — один вечер. Рантайм — меньше 1 КБ. Схема — обычный объект, из которого можно вырасти в любую сторону. Риск — молодость проекта и неполный разбор редких конструкций TS.

Брать или подождать: вердикт за 30 секунд

  • Брать, если фронт и бэк разведены, контракты плывут, а спецификации либо нет, либо она врёт.
  • Брать точечно, если хотите сначала проверить на одном эндпоинте без правок в архитектуре.
  • Подождать, если у вас работает OpenAPI-пайплайн с налаженным релизным циклом и ломается он редко.
  • Подождать, если в DTO тяжёлые типы — сначала прогоните их через генератор и посчитайте, сколько имён упадёт в getDegraded.

Вопросы, которые задают первыми

Нужно ли переписывать существующие типы?

Нет. Валидаторы строятся из тех DTO, что уже лежат в проекте. Правки нужны, только если типы описывают не то, что реально приходит.

Это работает только с React?

Привязки к фреймворку нет: модуль проверяет данные и чистит поля. Где вы делаете fetch, там и подключаете.

Схему можно отдать бэкенду?

Да, она сериализуется в JSON. Это удобный способ показать расхождение без спора о том, чья спецификация свежее.

А если библиотека перестанет развиваться?

У вас на руках JS-объекты со схемами. Их можно конвертировать в Zod или аналог и жить дальше. Привязки к рантайму почти нет — он весит меньше 1 КБ.

Что сделать на этой неделе

Откройте документацию (есть русская версия), возьмите один эндпоинт, который уже приносил проблемы. Прогоните через генератор, сравните полученную схему с тем, что вы думали об этом ответе раньше. Если расхождений ноль — валидация вам пока не нужна, и это хорошая новость. Если расхождения есть — вы только что нашли место, где приложение врёт пользователю молча.

Читайте также