- добавлен operationsTree для сборки полного клиента - удален режим генерации both - обновлена документация под npm SDK workflow - поднята версия пакета до 4.0.0
311 lines
10 KiB
Markdown
311 lines
10 KiB
Markdown
# @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
|