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

@gromlab/api-codegen

CLI для генерации typed TypeScript REST SDK из OpenAPI спецификации.

Генератор рассчитан на сценарий, где REST API можно вынести в отдельный npm-пакет, а приложения будут собирать из него свои API-клиенты: полный клиент, частичный клиент или точечные вызовы отдельных операций.

Главная Идея

По умолчанию @gromlab/api-codegen генерирует split-клиент.

Каждый endpoint становится отдельной typed operation-функцией. Это позволяет импортировать только нужные методы и не тащить весь API в bundle приложения.

Если нужен полный клиент, используйте operationsTree.

Если нужен клиент только под конкретный экран, модуль или приложение, соберите дерево операций вручную.

Если нужен максимально точечный вызов, импортируйте одну operation напрямую.

Быстрый Старт

npx @gromlab/api-codegen -i ./openapi.json -o ./src/generated

Для генерации из URL:

npx @gromlab/api-codegen -i https://api.example.com/openapi.json -o ./src/generated

По умолчанию будет создан split-клиент.

Что Генерируется

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.

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, соберите клиент вручную.

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 напрямую из ее файла:

import { v1AdminPharmaciesList } from './generated/operations/v1-admin-pharmacies-list';

Точечный Вызов Operation

Если по какой-то причине нужно вызвать только одну операцию, можно не собирать API-клиент через createApiClient и вызвать operation напрямую.

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.

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.

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-клиенты.

Пример структуры пакета:

packages/my-api/
├── package.json
└── src/
    ├── generated/
    └── index.ts

src/index.ts:

export * from './generated';

Использование в приложении:

import { createApiClient, HttpClient, operationsTree } from '@company/my-api';

const http = new HttpClient({
  baseUrl: 'https://api.example.com',
});

export const api = createApiClient(http, operationsTree);

Если приложение хочет выбрать только часть API:

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

Режим по умолчанию.

api-codegen -i ./openapi.json -o ./src/generated

Эквивалентно:

api-codegen -i ./openapi.json -o ./src/generated --mode split

Используйте этот режим для новых проектов, npm SDK пакетов и приложений, где важны модульность и tree-shaking.

single

Legacy-режим, который генерирует один монолитный TypeScript файл.

api-codegen -i ./openapi.json -o ./src/generated --mode single -n MyApi

Результат:

generated/
└── MyApi.ts

Используйте single, если проект уже завязан на старый монолитный generated-клиент.

CLI

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 из локального файла:

api-codegen -i ./openapi.json -o ./src/generated

Split из URL:

api-codegen -i https://api.example.com/openapi.json -o ./src/generated

Legacy single файл:

api-codegen -i ./openapi.json -o ./src/generated --mode single -n MyApi

Лицензия

MIT

Description
No description provided
Readme MIT 282 KiB
Languages
TypeScript 68.6%
EJS 29%
JavaScript 2.4%