Тип: справочник · Статус: актуально · Источники истины:
packages/api/openapi/,packages/api/redocly.yaml,packages/api/openapi-ts.config.ts
Когда читать: чтобы изменить OpenAPI-операцию, перегенерировать API, выбрать public import или разобраться с SDK result, TanStack Query options, mocks и cache tags.
- Меняйте контракт в
packages/api/openapi/, а не файлы вpackages/api/codegen/. - После изменения запускайте полный
gen, tests@repo/apiи roottsc. - Каноническая версия контракта — OpenAPI 3.2.0. Не понижайте её и не создавайте скрытую 3.1-копию.
- В приложении импортируйте только public facets пакета;
@repo/api/codegen/*— internal.
Petstore-контракт проверяет генератор. Это пример, а не backend: Next.js не обслуживает /pets.
Точка входа — openapi.yaml. Остальные части подключаются
относительными $ref.
packages/api/openapi/
├── openapi.yaml
├── paths/
│ ├── pets.yaml
│ └── pets-by-id.yaml
└── components/
├── parameters/
├── responses/
└── schemas/
Пример содержит пять операций:
| Метод | Путь | operationId |
Успешный ответ |
|---|---|---|---|
GET |
/pets |
findPetsByStatus |
200 PetPage |
POST |
/pets |
createPet |
201 Pet |
GET |
/pets/{petId} |
getPetById |
200 Pet |
PATCH |
/pets/{petId} |
updatePet |
200 Pet |
DELETE |
/pets/{petId} |
deletePet |
204 |
findPetsByStatus принимает массив status; offset служит page parameter для Infinite Query.
Из корня репозитория:
npm --workspace @repo/api run lint:openapi
npm --workspace @repo/api run gen
npm --workspace @repo/api run test
npm run tscgen выполняет одну последовательность:
openapi/openapi.yaml и $ref-файлы
→ Redocly bundle → bundled.yaml
→ Hey API → очищенный codegen/
→ post-generation cache tags, mock routes и guarded client normalization
→ Oxfmt только для codegen/
→ root TypeScript check, включающий codegen/
Отдельные команды bundle, generate:client, generate:helpers и format:generated нужны для
диагностики конкретного этапа. Обычное изменение контракта должно проходить через полный gen.
format:generated делает два прохода Oxfmt только по codegen/: для текущей формы nested client
types первый проход оставляет три стабильных rewrite для второго. Удаляйте второй проход, когда
oxfmt --check codegen проходит сразу после одного форматирования на зафиксированной версии Oxfmt.
Post-generator читает тот же Redocly bundle и извлекает операции, tags, paths, параметры и
успешные ответы независимо от внутреннего API Hey API. Он проверяет наличие нужной Faker factory
для каждого mock route с body. Для 204 factory не нужна: generated route возвращает нативный
Response без body.
Текущий parser сам не отклоняет операцию без tags или responses и может создать fallback route без
body со статусом 200. Обязательность этих полей контролируйте OpenAPI lint и review; не считайте
успешный post-generator отдельным доказательством полноты контракта.
- runtime проекта: Node.js 24;
- application compiler:
typescript@7.0.2; - generator:
@hey-api/openapi-ts@0.99.0; - локальный compiler workspace
@repo/api:typescript@6.0.3.
Версия Hey API 0.99.0 падает при загрузке с TypeScript 7.0.2 до генерации. Поэтому
openapi-ts.config.ts при прямом запуске регистрирует Node module-resolution hook, который только
в codegen process направляет импорт typescript на локальную версию 6.0.3. При обычном импорте
файл остаётся config без side effects. Root tsc, Next.js и tests продолжают использовать
TypeScript 7; root tsconfig.json включает generated output.
@hey-api/json-schema-ref-parser@1.4.4 фиксирует js-yaml@4.2.0, уязвимый к
CVE-2026-59869. Root overrides заменяет его
на первую исправленную версию ветки 4.x — 4.3.0; unit test проверяет фактически разрешённую из
parser package версию. Удаляйте override после того, как Hey API перестанет требовать уязвимый
exact pin и полный npm audit останется чистым без него.
Удаляйте локальную версию 6.0.3 и hook только после того, как новая exact-версия Hey API:
- запускается напрямую с project TypeScript 7;
- принимает текущий Redocly bundle OpenAPI 3.2;
- проходит полный
gen, generator parity tests и roottsc; - даёт чистый diff после второго последовательного
gen.
Guarded post-generation normalization сохраняет data: undefined в non-throwing error result и
для успешного 204. Если upstream client изменит ожидаемую форму ветвей, gen остановится вместо
молчаливого изменения публичной семантики; после upgrade нужно изучить diff и удалить ставший
ненужным workaround либо обновить его вместе с regression tests.
| Путь | Коммитить | Менять вручную | Назначение |
|---|---|---|---|
openapi/ |
Да | Да | Источник истины |
redocly.yaml, openapi-ts.config.ts |
Да | Да | Конфигурация pipeline |
openapi-ts.config.ts, generators/ |
Да | Да | Generation и post-generation |
Public facets и client-config.ts |
Да | Да | Стабильный API и runtime transport |
bundled.yaml |
Нет | Нет | Временный bundle Redocly |
codegen/ |
Да | Нет | Generated implementation |
Hey API запускается с output.clean: true: следующий gen удаляет ручные и устаревшие файлы из
codegen/. Меняйте источник, config или post-generator, затем регенерируйте output.
| Путь | Содержимое |
|---|---|
types.gen.ts |
OpenAPI types и as const enum objects |
sdk.gen.ts |
Flat SDK с path/query/body options |
client/, client.gen.ts |
Next.js Fetch client и настроенный instance |
zod.gen.ts |
Zod 4 request/response schemas |
@tanstack/react-query.gen.ts |
Query/mutation/infinite options и tagged keys |
@faker-js/faker.gen.ts |
Typed Faker factories |
cache-tags/ |
Server-only cache tag namespaces |
mock-client-routes.ts |
Method/path/status → Faker factory |
Внутренние numeric aliases вроде Pet2, порядок файлов и scoped plugin paths не являются
application contract.
import type { Pet } from '@repo/api'
import { getPetById, PetStatus } from '@repo/api'
import { client, createClient } from '@repo/api/client'
import { pets } from '@repo/api/cache-tags' // только server
import { fakePet } from '@repo/api/mocks'
import { getPetByIdOptions } from '@repo/api/query'
import { zPet } from '@repo/api/schemas'Доступны ровно шесть facets: @repo/api, /client, /query, /schemas, /mocks и
/cache-tags. Package exports направляет их на соответствующие generated entrypoints и два
ручных adapter-файла. Не добавляйте deep import в codegen/ и не используйте numeric aliases:
они остаются деталями конкретной версии генератора.
Path-параметры передаются в path, query-параметры — в query, JSON body — в body:
const result = await getPetById({
path: { petId: 'pet_123' },
})
if (result.error !== undefined) {
// result.data === undefined; result.error типизирован по documented responses.
return
}
result.data.name
result.responseПо умолчанию HTTP error не бросается: SDK возвращает { data: undefined, error, response }.
Если workflow использует exceptions, включите throwOnError:
const { data, response } = await getPetById({
path: { petId: 'pet_123' },
throwOnError: true,
})Успешный JSON response проходит generated Zod validation. Невалидный payload отклоняется с
ZodError. DELETE с documented status 204 возвращает data: undefined и исходный Response.
Нативные Fetch/Next.js options передаются вместе с SDK options:
await getPetById({
cache: 'force-cache',
next: { revalidate: 60, tags: ['pets', 'pets:petId:pet_123'] },
path: { petId: 'pet_123' },
})Generator создаёт options factories, а не готовые use... hooks:
'use client'
import { createPetMutation, getPetByIdOptions } from '@repo/api/query'
import { useMutation, useQuery } from '@tanstack/react-query'
const pet = useQuery(getPetByIdOptions({ path: { petId } }))
const create = useMutation(createPetMutation())
create.mutate({ body: { name: 'Pixel' } })Query и mutation keys включают OpenAPI tags. Для offset pagination компонуйте generated infinite options с policy конкретного экрана:
const pets = useInfiniteQuery({
...findPetsByStatusInfiniteOptions({
query: { limit: 20, status: ['available'] },
}),
initialPageParam: 0,
getNextPageParam: (lastPage) => {
const nextOffset = lastPage.offset + lastPage.limit
return nextOffset < lastPage.total ? nextOffset : undefined
},
})Numeric pageParam заменяет только query.offset; filters, limit, headers, path и остальные
options сохраняются. Импортируйте useInfiniteQuery и factory явно в реальном компоненте.
- Добавьте schemas, parameters и responses в
openapi/components/. - Опишите path item в
openapi/paths/и подключите его вopenapi.yaml. - Укажите уникальный
operationId, обязательныйsummary, корректные tags и responses. - Запустите
lint:openapiи полныйgen. - Проверьте generated и package exports diff: names, optionality, statuses, query keys, mocks и cache tags.
- Запустите tests
@repo/apiи roottsc. - Повторите
genи убедитесь, что второй запуск ничего не меняет.
Не копируйте generated Zod schemas в src/schemas. Для tests и Storybook используйте factories
из @repo/api/mocks, если нужная уже существует.