--- title: Автогенерация REST-клиента description: Генерация REST-клиента из OpenAPI-спецификации. keywords: [rest, openapi, api-codegen, автогенерация, generated, npx] --- # Автогенерация REST-клиента Генерация REST-клиента из OpenAPI-спецификации. ## Когда использовать Автогенерация используется, когда у API есть актуальная OpenAPI-спецификация. Генератор создаёт TypeScript-клиент, типы и методы API, а разработчик вручную добавляет настройку клиента и GET-хуки. ## Пример API В примерах используется Swagger Petstore: ```text https://petstore3.swagger.io/api/v3/openapi.json ``` Имена модуля: ```text src/infra/pet-store-api/ petStoreApi pet-store-api.generated.ts ``` ## Скрипт генерации `@gromlab/api-codegen` не устанавливается в `devDependencies`. Используем `npx @gromlab/api-codegen@latest`, чтобы запускать свежую версию. ```json { "scripts": { "codegen:pet-store-api": "npx @gromlab/api-codegen@latest -i https://petstore3.swagger.io/api/v3/openapi.json -o src/infra/pet-store-api/generated -n pet-store-api.generated" } } ``` Параметры: - `-i` — путь к OpenAPI-спецификации: URL или локальный файл. - `-o` — директория для сгенерированного файла. - `-n` — имя сгенерированного файла без `.ts`. Ключ `--swr` не используется. GET-хуки REST-клиента пишутся вручную, чтобы сохранить проектный контракт: один GET-хук = один GET-метод, без бизнес-логики и композиции. ## Генерация ```bash npm run codegen:pet-store-api ``` Ожидаемый результат: ```text src/infra/pet-store-api/generated/ └── pet-store-api.generated.ts ``` Сгенерированный файл не правится руками и коммитится в репозиторий. ## Проверка методов После генерации откройте `generated/pet-store-api.generated.ts` и проверьте фактические имена методов. Для Petstore нужны GET-операции вида: ```ts petStoreApi.pet.findPetsByStatus({ status: StatusEnum.Available }) petStoreApi.pet.getPetById({ petId: 10 }) ``` Точные сигнатуры зависят от OpenAPI-схемы и версии генератора. В рабочих задачах всегда сверяйтесь с generated-файлом. ## Алгоритм для агента После генерации агент должен действовать по шагам: 1. Открыть `generated/{service-name}.generated.ts`. 2. Найти фактические имена GET-методов клиента. 3. Для каждого нужного GET-метода найти generated-тип параметров и тип ответа. 4. Создать или обновить `client.ts` только для настройки транспорта и экспорта инстанса клиента. 5. Создать GET-хуки только для реально нужных GET-методов, не для всех методов API на всякий случай. 6. Для каждого GET-хука создать key-функцию формата `[serviceName, endpoint]`. 7. В key-функции вернуть `null`, если обязательные параметры не готовы. 8. В хуке принять `params?: GeneratedParams | null` и `config?: SWRConfiguration`. 9. В fetcher вызвать generated-метод клиента с `params as GeneratedParams`. 10. Экспортировать хук и key-функцию из `hooks/index.ts`. 11. Экспортировать наружу только нужные generated-типы, generated enum, DTO и `hooks` через корневой `index.ts`. Что агент не должен делать: - Не использовать ключ `--swr` генератора. - Не править `generated/*.generated.ts` руками. - Не добавлять GET-хуки для POST, PUT, PATCH, DELETE. - Не добавлять бизнес-флаги, тосты, редиректы и UI-состояние в GET-хук. - Не создавать словари enum-маппинга внутри GET-хука. - Не объявлять DTO и response-типы в файле хука. - Не вызывать `useSWR` условно. - Не добавлять `throw` в fetcher для неготовых params. ## `client.ts` Сгенерированный код не должен напрямую использоваться из приложения. Сначала создаётся настроенный инстанс клиента. ```ts // src/infra/pet-store-api/client.ts import { Api, HttpClient } from './generated/pet-store-api.generated' const baseUrl = process.env.NEXT_PUBLIC_PET_STORE_API_BASE_URL if (!baseUrl) { throw new Error('NEXT_PUBLIC_PET_STORE_API_BASE_URL is required') } const httpClient = new HttpClient({ baseUrl, baseApiParams: { secure: false, headers: { 'Content-Type': 'application/json', }, }, }) export const petStoreApi = new Api(httpClient) ``` Локальное значение `NEXT_PUBLIC_PET_STORE_API_BASE_URL` задаётся в `.env.local`. Не добавляйте fallback вроде `?? 'http://localhost:8080/api/v3'` или `?? ''`: если env-переменная не задана, клиент должен падать с явной ошибкой конфигурации. `client.ts` не содержит расширения типов, `declare module` и `Extended`-типы. Он только настраивает транспорт и экспортирует инстанс клиента. ## GET-хуки GET-хуки пишутся вручную после проверки generated-методов. Пример для generated-метода `petStoreApi.pet.getPetById({ petId })`: ```ts // src/infra/pet-store-api/hooks/use-get-pet-detail.hook.ts import type { SWRConfiguration } from 'swr' import useSWR from 'swr' import { petStoreApi } from '../client' import type { GetPetByIdParams, Pet } from '../generated/pet-store-api.generated' export const getPetDetailKey = (params?: GetPetByIdParams | null) => { if (!params?.petId) { return null } return ['pet-store-api', `/pet/${params.petId}`] as const } /** * Получает детальную карточку питомца с кешированием результата. */ export const useGetPetDetail = ( params?: GetPetByIdParams | null, config?: SWRConfiguration, ) => { const key = getPetDetailKey(params) const fetcher = () => petStoreApi.pet.getPetById(params as GetPetByIdParams) return useSWR(key, fetcher, config) } ``` Подробный контракт key-функций, `params`, `config` и запретов описан в разделе [GET-хуки REST-клиента](./hooks.md). ## Расширение сгенерированных типов Сгенерированный файл не правится руками. Если OpenAPI-спецификация неполная или генератор дал слишком общий тип (`object`, `unknown`, отсутствующее поле), расширения живут в `types/`. ```text src/infra/biocad-less-api/ ├── generated/ │ └── biocad-less-api.generated.ts ├── types/ │ ├── term.ts │ └── index.ts ├── client.ts └── index.ts ``` Пример расширения generated-типа: ```ts // src/infra/biocad-less-api/types/term.ts import type { TermRecordItem } from '../generated/biocad-less-api.generated' declare module '../generated/biocad-less-api.generated' { interface TermRecordItem { media?: { file?: string title?: string url?: string } } } export type TermRecordItemExtended = Omit< TermRecordItem, 'categories' | 'tags' | 'fields' > & { categories?: Array<{ _id?: string id?: string slug?: string name?: string }> tags?: Array<{ _id?: string id?: string slug?: string name?: string }> fields?: Record } ``` ```ts // src/infra/biocad-less-api/types/index.ts export type { TermRecordItemExtended } from './term' ``` `declare module` используется для добавления отсутствующих полей в generated-интерфейс. `Extended`-тип используется, когда нужно переопределить неточные поля, не трогая generated-файл. ## Публичный API ```ts // src/infra/pet-store-api/index.ts export { petStoreApi } from './client' export type { FindPetsByStatusParams, GetPetByIdParams, Pet, } from './generated/pet-store-api.generated' export { PetStatusEnum, StatusEnum } from './generated/pet-store-api.generated' export * from './hooks' ``` Наружу импортируют только из `infra/pet-store-api`, не из `generated/`. Если у модуля есть расширенные типы, они тоже реэкспортируются через `index.ts`: ```ts // src/infra/biocad-less-api/index.ts export type { TermRecordItemExtended } from './types' ``` ## Регенерация При изменении OpenAPI-схемы: ```bash npm run codegen:pet-store-api ``` Что меняется: - `generated/pet-store-api.generated.ts` — перезаписывается генератором. - `client.ts`, `hooks/`, `types/`, `index.ts` — не трогаются автоматически. Если после регенерации поменялись сигнатуры методов или типы, это исправляется в ручном коде модуля. ## Следующий шаг После генерации и настройки `client.ts` проверьте [использование REST-клиента](../usage.md) или добавьте [GET-хук REST-клиента](./hooks.md) для Client Components.