Files
api-codegen/README.md
S.Gromov fe5d3ae091 feat: сделать split-клиент режимом по умолчанию
- добавлен operationsTree для сборки полного клиента
- удален режим генерации both
- обновлена документация под npm SDK workflow
- поднята версия пакета до 4.0.0
2026-06-30 10:46:15 +03:00

311 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# @gromlab/api-codegen
CLI для генерации typed TypeScript REST SDK из OpenAPI спецификации.
Генератор рассчитан на сценарий, где REST API можно вынести в отдельный npm-пакет, а приложения будут собирать из него свои API-клиенты: полный клиент, частичный клиент или точечные вызовы отдельных операций.
## Главная Идея
По умолчанию `@gromlab/api-codegen` генерирует split-клиент.
Каждый endpoint становится отдельной typed operation-функцией. Это позволяет импортировать только нужные методы и не тащить весь API в bundle приложения.
Если нужен полный клиент, используйте `operationsTree`.
Если нужен клиент только под конкретный экран, модуль или приложение, соберите дерево операций вручную.
Если нужен максимально точечный вызов, импортируйте одну operation напрямую.
## Быстрый Старт
```bash
npx @gromlab/api-codegen -i ./openapi.json -o ./src/generated
```
Для генерации из URL:
```bash
npx @gromlab/api-codegen -i https://api.example.com/openapi.json -o ./src/generated
```
По умолчанию будет создан split-клиент.
## Что Генерируется
```text
generated/
├── create-api-client.ts
├── data-contracts.ts
├── http-client.ts
├── index.ts
├── operations-tree.ts
└── operations/
├── index.ts
├── get-users.ts
└── create-user.ts
```
Основные части:
- `http-client.ts` - fetch-based HTTP клиент, настройки авторизации, headers, baseUrl и transport customization.
- `data-contracts.ts` - TypeScript типы из OpenAPI schemas.
- `operations/*.ts` - отдельная typed function на каждый endpoint.
- `operations/index.ts` - barrel export всех операций.
- `operations-tree.ts` - дерево всех операций для сборки полного API клиента.
- `create-api-client.ts` - helper, который привязывает дерево операций к конкретному HTTP клиенту.
- `index.ts` - основная входная точка generated SDK.
## Полный Клиент
Если приложению нужен весь API, используйте `operationsTree`.
```typescript
import { createApiClient, HttpClient, operationsTree } from './generated';
// 1. Инициализация HTTP-клиента: baseUrl, headers, авторизация и transport-настройки.
const http = new HttpClient({
baseUrl: 'https://api.example.com',
});
// 2. Создание API-клиента: привязываем все сгенерированные операции к HTTP-клиенту.
const api = createApiClient(http, operationsTree);
// 3. Использование API-клиента: вызываем методы из дерева операций.
const users = await api.users.getAll({});
const createdUser = await api.users.create({ email, password });
```
`operationsTree` намеренно лежит в отдельном файле и экспортируется отдельно. Импортируя его, вы явно выбираете полный клиент со всеми операциями.
## Частичный Клиент
Если приложению нужна только часть API, соберите клиент вручную.
```typescript
import { createApiClient, HttpClient } from './generated';
import {
v1AdminPharmaciesCreate,
v1AdminPharmaciesList,
v1AdminPharmaciesProfileUpdate,
} from './generated/operations';
const http = new HttpClient({
baseUrl: 'https://api.example.com',
});
const api = createApiClient(http, {
pharmaciesAdmin: {
list: v1AdminPharmaciesList,
create: v1AdminPharmaciesCreate,
updateProfile: v1AdminPharmaciesProfileUpdate,
},
});
const pharmacies = await api.pharmaciesAdmin.list({});
const createdPharmacy = await api.pharmaciesAdmin.create(payload);
await api.pharmaciesAdmin.updateProfile({ id }, payload);
```
Так можно собрать отдельный клиент для админки, публичного сайта, мобильного приложения или отдельного feature-модуля.
`operations/index.ts` реэкспортит все operation-функции, поэтому можно делать named imports из `./generated/operations`. В ESM-сборке такой импорт остается tree-shaking friendly: в клиенте используются только явно выбранные операции.
Если OpenAPI большой и полный SDK занимает десятки тысяч строк, приложение не обязано тянуть весь набор методов в свой чанк. Можно собрать минимально рабочий API-клиент под конкретный сценарий.
Если нужен максимально строгий контроль над тем, какой файл попадет в bundle, импортируйте operation напрямую из ее файла:
```typescript
import { v1AdminPharmaciesList } from './generated/operations/v1-admin-pharmacies-list';
```
## Точечный Вызов Operation
Если по какой-то причине нужно вызвать только одну операцию, можно не собирать API-клиент через `createApiClient` и вызвать operation напрямую.
```typescript
import { HttpClient } from './generated/http-client';
import { v1AdminPharmaciesList } from './generated/operations/v1-admin-pharmacies-list';
const http = new HttpClient({
baseUrl: 'https://api.example.com',
});
const pharmacies = await v1AdminPharmaciesList(http, {});
```
Это полезно для разовых сценариев: health-check, bootstrap-запроса, утилитного скрипта или кода, где полноценный объект `api` не нужен.
## Кастомный HTTP Клиент
`HttpClient` создается отдельно и передается в generated operations. Это позволяет централизованно настраивать авторизацию, headers, baseUrl и transport behavior.
```typescript
import { HttpClient } from './generated';
type SecurityData = {
token: string;
};
const http = new HttpClient<SecurityData>({
baseUrl: 'https://api.example.com',
baseApiParams: {
headers: {
'X-App-Version': '1.0.0',
},
},
securityWorker: (securityData) => {
if (!securityData?.token) {
return undefined;
}
return {
headers: {
Authorization: `Bearer ${securityData.token}`,
},
};
},
});
http.setSecurityData({ token: 'jwt-token' });
```
Для отдельного вызова можно передать дополнительные request params последним аргументом operation.
```typescript
await api.users.getAll(
{},
{
headers: {
'X-Request-Id': requestId,
},
},
);
```
## API Как npm-Пакет
Типичный workflow:
1. Backend публикует OpenAPI спецификацию.
2. Отдельный пакет генерирует typed REST SDK в `src/generated`.
3. Пакет экспортирует generated entrypoint.
4. Приложения импортируют SDK и собирают нужные API-клиенты.
Пример структуры пакета:
```text
packages/my-api/
├── package.json
└── src/
├── generated/
└── index.ts
```
`src/index.ts`:
```typescript
export * from './generated';
```
Использование в приложении:
```typescript
import { createApiClient, HttpClient, operationsTree } from '@company/my-api';
const http = new HttpClient({
baseUrl: 'https://api.example.com',
});
export const api = createApiClient(http, operationsTree);
```
Если приложение хочет выбрать только часть API:
```typescript
import { createApiClient, HttpClient } from '@company/my-api';
import {
v1AdminPharmaciesList,
v1AdminPharmaciesProfileUpdate,
} from '@company/my-api/operations';
export const api = createApiClient(new HttpClient(), {
pharmaciesAdmin: {
list: v1AdminPharmaciesList,
updateProfile: v1AdminPharmaciesProfileUpdate,
},
});
```
## Режимы Генерации
### split
Режим по умолчанию.
```bash
api-codegen -i ./openapi.json -o ./src/generated
```
Эквивалентно:
```bash
api-codegen -i ./openapi.json -o ./src/generated --mode split
```
Используйте этот режим для новых проектов, npm SDK пакетов и приложений, где важны модульность и tree-shaking.
### single
Legacy-режим, который генерирует один монолитный TypeScript файл.
```bash
api-codegen -i ./openapi.json -o ./src/generated --mode single -n MyApi
```
Результат:
```text
generated/
└── MyApi.ts
```
Используйте `single`, если проект уже завязан на старый монолитный generated-клиент.
## CLI
```bash
api-codegen -i <input> -o <output> [--mode split|single] [-n <name>]
```
Аргументы:
- `-i, --input <path>` - путь к OpenAPI файлу или URL.
- `-o, --output <path>` - директория для generated файлов.
- `--mode <mode>` - режим генерации: `split` или `single`. По умолчанию `split`.
- `-n, --name <name>` - имя файла для `single` режима без расширения `.ts`.
- `--single-file` - устаревший алиас для `--mode single`.
## Примеры Команд
Split из локального файла:
```bash
api-codegen -i ./openapi.json -o ./src/generated
```
Split из URL:
```bash
api-codegen -i https://api.example.com/openapi.json -o ./src/generated
```
Legacy single файл:
```bash
api-codegen -i ./openapi.json -o ./src/generated --mode single -n MyApi
```
## Лицензия
MIT