Внедрение автоматизации кода API на основе Orval
1. Введение
В фронтенд-разработке интеграция с API и написание типов — это частая и повторяющаяся задача. Каждый раз, когда добавляется новый API или изменяются параметры запроса и структура ответа, необходимо вносить изменения в определения типов, писать функции для вызова API и настраивать хуки TanStack Query. Когда количество API невелико, писать их вручную не представляет собой больших трудностей, но по мере увеличения масштаба проекта количество файлов для изменения возрастает, и в этом процессе легко возникают человеческие ошибки, такие как неправильное написание имени поля или несоответствие типов.
В частности, в среде, где бэкенд и фронтенд разрабатываются параллельно, документация Swagger может постоянно изменяться. В этом случае, если фронтенд управляет типами и функциями вызова вручную, это может привести к тому, что фактические спецификации API и код будут различаться. Например, если изменилось имя поля ответа или изменился статус опциональности, и были изменены только некоторые файлы, это может привести к ошибкам во время выполнения или ошибкам TypeScript.
Чтобы уменьшить такие проблемы, в проектах, в которых я участвовал, мы внедрили Orval, инструмент для автоматической генерации кода API на основе документации Swagger. Orval может генерировать типы TypeScript и функции вызова API на основе спецификаций OpenAPI или Swagger и, в зависимости от настроек, также может создавать хуки на основе TanStack Query.
Цель внедрения заключалась не только в быстром создании кода, но и в снижении несоответствий между спецификацией API и фронтенд-кодом. Основной задачей было автоматизировать повторяющийся код-шаблон, чтобы разработчики могли больше сосредоточиться на логике интерфейса, управлении состоянием, обработке исключений и улучшении пользовательского опыта.
Кроме того, в условиях монорепозитория часто несколько приложений или пакетов делят общие типы API и логику вызовов. С помощью Orval можно создать общие API-слои на основе Swagger и использовать их в каждом пакете, что позволяет сократить количество дублирующих написаний и снизить нагрузку на обслуживание.
2. Реальные проблемы, возникшие при применении
Одной из основных проблем, с которой мы столкнулись при использовании Orval, было то, что в окружении монорепозитория версия TanStack Query не распознается должным образом, что приводит к ошибкам типов.
Проект управлялся с помощью комбинации монорепозитория и pnpm. В этой структуре зависимости часто декларируются только для конкретных пакетов, которые действительно используются, вместо установки всех зависимостей в корневую папку. На тот момент TanStack Query также был установлен только в пакетах, использующих реальные API хуки, и не существовал в корне монорепозитория.
Проблема заключалась в том, что команда выполнения Orval выполнялась из корня монорепозитория. Orval определяет среду проекта на основе места выполнения, поэтому он не мог правильно обнаружить установленную версию TanStack Query v5 в пакетах, использующих реальные API хуки, находящихся в корне. В результате код, созданный тогда, был сгенерирован на основе v4, а не v5, что вызвало конфликт типов объекта options, используемого в реальном проекте с TanStack Query v5 и привело к ошибке TypeScript.
Сначала это казалось просто проблемой типа сгенерированного кода, но, выяснив причину, стало понятно, что ключевым моментом было то, как Orval определял версию. С точки зрения разработчика проект использует TanStack Query v5, но инструмент не смог найти эту зависимость из корневого каталога, где он выполнялся. То есть, фактическая среда использования и среда выполнения, определяемая инструментом, различались.
В этой проблеме стоит отметить ещё один момент: если бы попытались решить это напрямую, изменив сгенерированные файлы, в следующий раз при выполнении Orval, вероятно, возникли бы те же проблемы. Автоматически сгенерированные файлы перезаписываются при регенерации, поэтому изменение результата генерации не может быть коренным решением.
3. Анализ причин и решений
Причиной данного вопроса является различие между местоположением выполнения Orval и фактическим местоположением зависимостей, а также то, что не удалось четко контролировать генерацию кода в соответствии с TanStack Query v5. Автоматизированные инструменты удобны, но нельзя сказать, что они работают, полностью понимая структуру проекта. Особенно в случае с монорепозиториями, рабочими пространствами pnpm и управлением зависимостями по пакетам структура может быть сложной, и результаты могут не соответствовать ожидаемым только на основе настроек по умолчанию.
Когда возникла проблема, было три решения, которые можно было рассмотреть.
Первый способ — изменить местоположение выполнения Orval на основании установленного пакета TanStack Query. Отрегулировав выполнение скрипта в соответствующем каталоге пакета, Orval сможет правильно обнаружить зависимости.
Во-вторых, это способ добавить зависимость TanStack Query в корневой проект, чтобы Orval мог обнаружить версию. Однако этот подход подразумевает добавление зависимости в места, где она фактически не используется в монорепозитории, поэтому следует учитывать, что это может конфликтовать с принципами управления зависимостями.
В-третьих, это способ указать в конфигурационном файле Orval критерии создания хуков TanStack Query.
Таким образом, мы на самом деле решили это, указав в настройках Orval, чтобы он создавался на основе TanStack Query v5. Этот метод имеет преимущество в том, что он позволяет четко контролировать критерии генерации кода, не сильно меняя структуру зависимостей. Кроме того, поскольку намерение остается в конфигурационном файле, команде будет легче выяснить причины, если они столкнутся с той же проблемой позже.
Кроме того, стоит рассмотреть возможность разделения автоматически сгенерированного кода и кода, используемого на реальном экране. Вместо того чтобы непосредственно использовать функции и хуки API, созданные Orval, можно применить подход, при котором они оборачиваются в custom hook или service layer, если это необходимо, что может улучшить поддерживаемость. Сгенерированный код остается в области, близкой к спецификации API, а обработка необходимых данных, настройка основных параметров, обработка ошибок и т. д. управляются на отдельном уровне, что позволяет снизить влияние на экранный код даже при повторной генерации Orval и разделить ответственность за изменения API и разработку экрана.
4. Введение эффектов
Самым большим эффектом внедрения Orval стало сокращение повторяющихся задач. Мы смогли уменьшить количество повторяющихся задач, связанных с ручным определением типов каждый раз, когда добавляется API, и непосредственной записью функций вызова API. Настройка Orval на основе React Query также позволяет создавать хуки TanStack Query, что значительно снижает нагрузку по написанию базового кода интеграции API.
Вторым эффектом было снижение человеческих ошибок. При ручном вводе типов могут возникать ошибки, такие как неправильный ввод имени поля, пропуск некоторых элементов структуры ответа или неверная оценка необходимости опциональности. Использование Orval позволяет создавать типы на основе спецификации Swagger, что снижает несоответствия между спецификацией API и типами на фронтенде.
Третьим эффектом является более легкое отслеживание изменений API. Если работать по принципу коммита автоматически сгенерированных файлов, можно увидеть с помощью Git diff, какие типы или функции изменились из-за изменений Swagger. Таким образом, изменения в пути API, теле запроса, типе ответа и т.д. можно рассмотреть на основе кода.
Также это преимущество в том, что можно разделить код и экранную логику на основе спецификации API. Сгенерированный код можно оставить на уровне API, а фактический экран можно организовать через отдельные пользовательские хуки или сервисы, чтобы разделить ответственность за изменения API и разработку экрана.
5. Важные моменты, на которые следует обратить внимание в процессе эксплуатации
Для надежного использования Orval необходимо соблюдение нескольких критериев.
Сначала нужно определить момент автоматического генерации. Если его запускать автоматически в момент сборки, то это всегда будет отражать последние спецификации, но могут внезапно вступить в силу неожиданные изменения API, которые могут сломать сборку. С другой стороны, метод, при котором разработчик запускает процесс вручную и проверяет созданные файлы перед коммитом, имеет преимущество в том, что позволяет проверить внесенные изменения, но в случае пропуска запуска, спецификации и код могут различаться. Необходимо выбрать подходящее решение в зависимости от ситуации в текущем проекте и стиля работы команды.
Также необходимо решить, на какой Swagger среде будет основано создание. Спецификации Swagger для разработки, QA и эксплуатации могут различаться, поэтому требуется согласие внутри команды. Среда разработки быстро отражает последние изменения, но может включать незавершенные API; эксплуатационная среда стабильна, но может не содержать последние разработки. В противном случае могут быть непреднамеренные изменения спецификаций, что, в свою очередь, может увеличить время на отладку.
Также необходимо контролировать диапазон создания. Если создать все API, включенные в документацию Swagger, за один раз, это может привести к тому, что код станет ненужным, включая API, которые на самом деле не используются на фронтенде. Поэтому лучше создавать только необходимые API на основе тегов, путей или доменов.
Напоследок, рекомендуется не вносить изменения непосредственно в автоматически сгенерированные файлы, так как изменения могут быть перезаписаны при следующем создании. Если вам нужна индивидуальная логика, безопаснее обрабатывать её в отдельном оболочке или пользовательском хуке, чем изменять файл генерации.
6. Завершение
На основе опыта внедрения Orval я понял, что автоматизированные инструменты являются полезными для сокращения рутинных задач, но для их надежного использования разработчик должен четко понимать и контролировать структуру проекта и среду выполнения.
Особенно в среде монорепо и pnpm результаты работы инструмента могут варьироваться в зависимости от места выполнения, местоположения объявления зависимостей и структуры пакетов. Эта проблема выглядела как простая ошибка типа, но на самом деле Orval не смог правильно обнаружить версию TanStack Query, в результате чего был сгенерирован код, соответствующий версии v4, что вызвало конфликт с типами v5, которые использовались в реальном проекте.
В результате, Orval стал полезным инструментом, который автоматизирует работу с интеграцией API и позволяет последовательно управлять типами, кодами вызова API и даже хуками TanStack Query на основе Swagger, в зависимости от настроек. Однако для надежного использования также необходимы указание версии, разделение окружений, контроль диапазона создания и принципы управления созданными файлами.
Я считаю, что при внедрении автоматизированных инструментов необходимо не только учитывать преимущества инструмента, но и проверять, как он работает внутри структуры проекта и по каким критериям создаются результаты.
зелёный