📑 Содержание
- 1. Что такое кодогенерация из OpenAPI
- 2. Зачем генерировать код из спецификации
- 3. Обзор инструментов
- 4. TypeScript: openapi-typescript + openapi-fetch
- 5. Orval: React Query из OpenAPI
- 6. openapi-generator: универсальный генератор
- 7. Python-клиенты
- 8. Go-клиенты
- 9. Генерация серверных заглушек
- 10. CI/CD: автоматизация генерации
- 11. Best practices
- 12. Сравнительная таблица инструментов
- FAQ
1. Что такое кодогенерация из OpenAPI
Кодогенерация — автоматическое создание исходного кода на основе OpenAPI/Swagger-спецификации. Спецификация описывает эндпоинты, параметры, тела запросов, ответы и модели данных — генератор превращает это описание в готовый к использованию код.
YAML / JSON
CLI / плагин
клиенты, типы, SDK
Что можно генерировать
| Тип артефакта | Описание | Пример |
|---|---|---|
| Типы / интерфейсы | TypeScript interfaces, Python dataclasses, Go structs | interface User { id: string; name: string; } |
| API-клиент | Типизированные функции для вызова API | api.users.getById({ id: "123" }) |
| SDK | Полноценная библиотека с классами, ошибками, ретраями | new UsersApi(config).getUser("123") |
| Серверные заглушки | Скелет сервера с маршрутами и валидацией | Express/FastAPI/Spring контроллеры |
| React Query хуки | useQuery/useMutation хуки для каждого эндпоинта | useGetUser({ id: "123" }) |
| Mock-сервер | Сервер с фейковыми данными на основе спеки | Prism, MSW handlers |
2. Зачем генерировать код из спецификации
✅ Преимущества
- Единый источник правды — спецификация = документация = код
- Типобезопасность — ошибки ловятся при компиляции
- Скорость — не нужно писать HTTP-клиент руками
- Консистентность — все клиенты работают одинаково
- Автоматическое обновление — изменил спеку → перегенерировал
- Breaking changes — компилятор покажет несовместимости
⚠️ Ограничения
- Качество зависит от качества спецификации
- Сгенерированный код не всегда идиоматичен
- Нужна CI/CD интеграция для актуальности
- Кастомизация может быть сложной
- Размер сгенерированного кода (SDK)
- Зависимость от инструмента
3. Обзор инструментов
openapi-typescript
TypeScriptЛёгкий генератор только типов из OpenAPI 3.x. Не генерирует runtime-код — только .d.ts / TypeScript-типы. В паре с openapi-fetch даёт type-safe HTTP-клиент с минимальным бандлом (~6 KB).
Лучший выбор для TypeScript-проектов, где нужны типы без лишнего кода.
Orval
TypeScriptГенератор клиентского кода с интеграциями для React Query, SWR, Angular, Vue Query. Генерирует хуки useQuery/useMutation для каждого эндпоинта. Поддерживает Zod-схемы и MSW-моки.
Лучший выбор для React/Vue проектов с TanStack Query.
openapi-generator
50+ языковУниверсальный open-source генератор. Поддерживает TypeScript, Python, Go, Java, C#, Rust, Kotlin, Swift и десятки других. Генерирует полноценные SDK с классами, моделями, конфигурацией и обработкой ошибок.
Лучший выбор для мультиязычных проектов и генерации серверных заглушек.
swagger-codegen
40+ языковОригинальный генератор от SmartBear (создатели Swagger). Предшественник openapi-generator. Поддерживает Swagger 2.0 и OpenAPI 3.0. Сейчас менее активно развивается — для новых проектов рекомендуется openapi-generator.
4. TypeScript: openapi-typescript + openapi-fetch
Самый лёгкий и type-safe подход для TypeScript. Генерируются только типы, а openapi-fetch использует их для типизации запросов.
Установка
npm install openapi-typescript openapi-fetch
# или
npx openapi-typescript ./openapi.yaml -o ./src/api/schema.d.ts
Генерация типов
# Из локального файла
npx openapi-typescript ./openapi.yaml -o ./src/api/schema.d.ts
# Из URL
npx openapi-typescript https://api.example.com/openapi.json -o ./src/api/schema.d.ts
# С опциями
npx openapi-typescript ./openapi.yaml \
--output ./src/api/schema.d.ts \
--enum # генерировать TS enum вместо union
Пример OpenAPI-спецификации
# openapi.yaml
openapi: 3.1.0
info:
title: Blog API
version: 1.0.0
paths:
/posts:
get:
operationId: listPosts
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: limit
in: query
schema:
type: integer
default: 20
responses:
'200':
description: Список постов
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Post'
total:
type: integer
post:
operationId: createPost
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePost'
responses:
'201':
description: Пост создан
content:
application/json:
schema:
$ref: '#/components/schemas/Post'
/posts/{id}:
get:
operationId: getPost
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Post'
'404':
description: Not found
components:
schemas:
Post:
type: object
required: [id, title, content, createdAt]
properties:
id:
type: string
format: uuid
title:
type: string
content:
type: string
published:
type: boolean
createdAt:
type: string
format: date-time
CreatePost:
type: object
required: [title, content]
properties:
title:
type: string
minLength: 1
maxLength: 200
content:
type: string
published:
type: boolean
default: false
Сгенерированные типы
Результат: schema.d.ts
openapi-typescript генерирует точные TypeScript-типы для всех paths, operations, components:
// src/api/schema.d.ts (сгенерировано)
export interface paths {
"/posts": {
get: {
parameters: {
query?: { page?: number; limit?: number; };
};
responses: {
200: {
content: {
"application/json": {
data: components["schemas"]["Post"][];
total: number;
};
};
};
};
};
post: {
requestBody: {
content: {
"application/json": components["schemas"]["CreatePost"];
};
};
responses: {
201: {
content: {
"application/json": components["schemas"]["Post"];
};
};
};
};
};
"/posts/{id}": {
get: {
parameters: { path: { id: string; }; };
responses: {
200: {
content: {
"application/json": components["schemas"]["Post"];
};
};
404: { content: never; };
};
};
};
}
export interface components {
schemas: {
Post: {
id: string;
title: string;
content: string;
published?: boolean;
createdAt: string;
};
CreatePost: {
title: string;
content: string;
published?: boolean;
};
};
}
Использование с openapi-fetch
// src/api/client.ts
import createClient from 'openapi-fetch';
import type { paths } from './schema';
const api = createClient<paths>({
baseUrl: 'https://api.example.com',
headers: {
Authorization: `Bearer ${token}`,
},
});
// Полная типобезопасность — автокомплит для путей, параметров, тела, ответа
// GET /posts?page=2&limit=10
const { data, error } = await api.GET('/posts', {
params: { query: { page: 2, limit: 10 } },
});
// data.data → Post[]
// data.total → number
// POST /posts
const { data: newPost } = await api.POST('/posts', {
body: {
title: 'Новый пост',
content: 'Содержимое...',
published: true,
},
});
// newPost → Post
// GET /posts/{id}
const { data: post, error: notFound } = await api.GET('/posts/{id}', {
params: { path: { id: '550e8400-e29b-41d4-a716-446655440000' } },
});
// post → Post | undefined
// notFound → 404 error
fetch() с типизацией. Никакого лишнего runtime-кода, в отличие от полноценных SDK-генераторов.
5. Orval: React Query из OpenAPI
Orval — генерирует готовые React Query хуки, Zod-схемы и MSW-моки из OpenAPI.
Установка и конфигурация
npm install orval -D
// orval.config.ts
import { defineConfig } from 'orval';
export default defineConfig({
blogApi: {
input: {
target: './openapi.yaml',
// или URL: 'https://api.example.com/openapi.json'
},
output: {
target: './src/api/generated.ts',
client: 'react-query',
mode: 'tags-split', // файл на каждый тег
override: {
mutator: {
path: './src/api/custom-fetch.ts',
name: 'customFetch',
},
query: {
useQuery: true,
useInfinite: true,
useSuspenseQuery: true,
},
},
},
},
});
# Генерация
npx orval
# Watch-режим
npx orval --watch
Сгенерированные хуки
// src/api/generated.ts (сгенерировано Orval)
// Хуки для GET /posts
export const useListPosts = (
params?: ListPostsParams,
options?: UseQueryOptions<ListPosts200>,
) => {
return useQuery({
queryKey: getListPostsQueryKey(params),
queryFn: () => listPosts(params),
...options,
});
};
// Хук для POST /posts
export const useCreatePost = (
options?: UseMutationOptions<Post, Error, CreatePost>,
) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createPost,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: getListPostsQueryKey() });
},
...options,
});
};
// Типы
export interface Post {
id: string;
title: string;
content: string;
published?: boolean;
createdAt: string;
}
export interface CreatePost {
title: string;
content: string;
published?: boolean;
}
Использование в компонентах
// components/Posts.tsx
import { useListPosts, useCreatePost } from '@/api/generated';
export function Posts() {
const { data, isLoading } = useListPosts({ page: 1, limit: 20 });
const createPost = useCreatePost();
const handleCreate = () => {
createPost.mutate({
title: 'Новый пост',
content: 'Содержимое',
});
};
if (isLoading) return <div>Загрузка...</div>;
return (
<div>
{data?.data.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<time>{post.createdAt}</time>
</article>
))}
<button onClick={handleCreate}>Создать пост</button>
</div>
);
}
Генерация Zod-схем
// orval.config.ts
output: {
client: 'zod',
// Генерирует Zod-схемы для runtime-валидации
}
// Результат:
export const postSchema = z.object({
id: z.string().uuid(),
title: z.string(),
content: z.string(),
published: z.boolean().optional(),
createdAt: z.string().datetime(),
});
Генерация MSW-моков
// orval.config.ts
output: {
client: 'react-query',
mock: true, // генерирует MSW handlers
}
// Результат: src/api/generated.msw.ts
import { http, HttpResponse } from 'msw';
export const getListPostsHandler = () =>
http.get('*/posts', () => {
return HttpResponse.json({
data: [
{ id: '1', title: 'Mock Post', content: '...', createdAt: '2026-02-23' },
],
total: 1,
});
});
6. openapi-generator: универсальный генератор
openapi-generator — самый мощный генератор с поддержкой 50+ языков. Генерирует полноценные SDK, серверные заглушки и документацию.
Установка
# NPM (рекомендуется)
npm install @openapitools/openapi-generator-cli -g
# Docker
docker pull openapitools/openapi-generator-cli
# Homebrew (macOS)
brew install openapi-generator
TypeScript (axios)
openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-axios \
-o ./src/api/generated \
--additional-properties=supportsES6=true,npmName=blog-api-client
# Доступные TypeScript-генераторы:
# typescript-axios — Axios-клиент
# typescript-fetch — Fetch API
# typescript-node — Node.js (http)
# typescript-angular — Angular HttpClient
Использование сгенерированного SDK
// TypeScript (axios)
import { Configuration, PostsApi } from './api/generated';
const config = new Configuration({
basePath: 'https://api.example.com',
accessToken: 'your-jwt-token',
});
const postsApi = new PostsApi(config);
// Типизированные вызовы
const { data: posts } = await postsApi.listPosts(1, 20);
// posts.data → Post[]
const { data: newPost } = await postsApi.createPost({
title: 'Hello',
content: 'World',
});
// newPost → Post
// Обработка ошибок
try {
await postsApi.getPost('non-existent-id');
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
console.log('Пост не найден');
}
}
7. Python-клиенты
openapi-generator (Python)
openapi-generator-cli generate \
-i openapi.yaml \
-g python \
-o ./python-client \
--additional-properties=packageName=blog_api_client
# Использование
from blog_api_client import ApiClient, Configuration
from blog_api_client.api import PostsApi
config = Configuration(
host="https://api.example.com",
access_token="your-jwt-token"
)
with ApiClient(config) as client:
posts_api = PostsApi(client)
# Типизированные вызовы
posts = posts_api.list_posts(page=1, limit=20)
print(posts.data) # List[Post]
new_post = posts_api.create_post(
create_post={"title": "Hello", "content": "World"}
)
print(new_post.id) # str (UUID)
openapi-python-client (альтернатива)
pip install openapi-python-client
# Генерация
openapi-python-client generate --url https://api.example.com/openapi.json
# Обновление существующего
openapi-python-client update --url https://api.example.com/openapi.json
# Более идиоматичный Python-клиент
from blog_api_client import Client, AuthenticatedClient
from blog_api_client.api.posts import list_posts, create_post
from blog_api_client.models import CreatePost
client = AuthenticatedClient(
base_url="https://api.example.com",
token="your-jwt-token",
)
# Каждый эндпоинт — функция (не класс)
posts = list_posts.sync(client=client, page=1, limit=20)
# Async-версия
posts = await list_posts.asyncio(client=client, page=1, limit=20)
# Создание поста
new_post = create_post.sync(
client=client,
body=CreatePost(title="Hello", content="World"),
)
8. Go-клиенты
oapi-codegen (рекомендуется для Go)
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
# Генерация
oapi-codegen -generate types,client -package api openapi.yaml > api/client.go
// api/client.go (сгенерировано)
package api
type Post struct {
Id string `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Published *bool `json:"published,omitempty"`
CreatedAt string `json:"createdAt"`
}
type CreatePost struct {
Title string `json:"title"`
Content string `json:"content"`
Published *bool `json:"published,omitempty"`
}
// main.go — использование
package main
import (
"context"
"fmt"
"github.com/yourproject/api"
)
func main() {
client, err := api.NewClientWithResponses(
"https://api.example.com",
api.WithRequestEditorFn(func(ctx context.Context, req *http.Request) error {
req.Header.Set("Authorization", "Bearer "+token)
return nil
}),
)
if err != nil {
panic(err)
}
// Типизированные вызовы
page, limit := 1, 20
resp, err := client.ListPostsWithResponse(context.Background(), &api.ListPostsParams{
Page: &page,
Limit: &limit,
})
if resp.StatusCode() == 200 {
for _, post := range resp.JSON200.Data {
fmt.Printf("Post: %s\n", post.Title)
}
}
}
openapi-generator (Go)
openapi-generator-cli generate \
-i openapi.yaml \
-g go \
-o ./go-client \
--additional-properties=packageName=blogapi
9. Генерация серверных заглушек
Кодогенерация работает и в обратном направлении: из OpenAPI-спецификации можно создать скелет сервера с маршрутами, валидацией и моделями.
Доступные серверные генераторы
| Генератор | Фреймворк | Команда |
|---|---|---|
nodejs-express-server |
Node.js + Express | -g nodejs-express-server |
python-fastapi |
Python + FastAPI | -g python-fastapi |
go-server |
Go + net/http | -g go-server |
spring |
Java + Spring Boot | -g spring |
php-laravel |
PHP + Laravel | -g php-laravel |
rust-server |
Rust + Hyper | -g rust-server |
Пример: FastAPI server stub
openapi-generator-cli generate \
-i openapi.yaml \
-g python-fastapi \
-o ./server
# server/src/openapi_server/apis/posts_api.py (сгенерировано)
from fastapi import APIRouter, Path, Query
from ..models.post import Post
from ..models.create_post import CreatePost
router = APIRouter()
@router.get("/posts", response_model=ListPostsResponse)
async def list_posts(
page: int = Query(1, ge=1),
limit: int = Query(20, ge=1, le=100),
) -> ListPostsResponse:
# TODO: реализовать логику
...
@router.post("/posts", response_model=Post, status_code=201)
async def create_post(body: CreatePost) -> Post:
# TODO: реализовать логику
...
@router.get("/posts/{id}", response_model=Post)
async def get_post(id: str = Path(...)) -> Post:
# TODO: реализовать логику
...
10. CI/CD: автоматизация генерации
Самое ценное в кодогенерации — автоматическое обновление при изменении спецификации. Интегрируйте в CI/CD.
GitHub Actions
# .github/workflows/codegen.yml
name: OpenAPI Codegen
on:
push:
paths:
- 'openapi.yaml'
schedule:
- cron: '0 6 * * 1' # каждый понедельник
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Generate TypeScript types
run: npx openapi-typescript openapi.yaml -o src/api/schema.d.ts
- name: Generate React Query hooks
run: npx orval
- name: Check for changes
id: diff
run: |
git diff --exit-code src/api/ || echo "changed=true" >> $GITHUB_OUTPUT
- name: Create PR with updated types
if: steps.diff.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v6
with:
title: 'chore: update generated API types'
body: |
Auto-generated from OpenAPI spec changes.
**Review the type changes carefully — they may indicate breaking API changes.**
branch: chore/update-api-types
labels: automated, api
npm scripts
{
"scripts": {
"codegen": "openapi-typescript openapi.yaml -o src/api/schema.d.ts",
"codegen:orval": "orval",
"codegen:all": "npm run codegen && npm run codegen:orval",
"codegen:watch": "orval --watch",
"predev": "npm run codegen:all",
"prebuild": "npm run codegen:all"
}
}
Спецификация обновлена
Бэкенд-разработчик меняет openapi.yaml — добавляет поле, меняет тип, добавляет эндпоинт.
CI запускает генерацию
Автоматически генерируются новые типы и клиенты для всех языков.
Проверка breaking changes
Если типы изменились — TypeScript-компилятор покажет ошибки в клиентском коде. Breaking changes ловятся автоматически.
PR с изменениями
Создаётся Pull Request с обновлёнными типами. Ревьюер видит, что именно изменилось в API.
11. Best practices
1. Держите спецификацию актуальной
# Валидация спецификации
npx @redocly/cli lint openapi.yaml
# Или с spectral
npx @stoplight/spectral-cli lint openapi.yaml
2. Не редактируйте сгенерированный код
# .gitignore (если генерируете при сборке)
src/api/generated/
# Или коммитьте, но добавьте заголовок:
# AUTO-GENERATED — do not edit manually
# Re-generate: npm run codegen
Если нужна кастомизация — используйте шаблоны (templates) генератора или обёртку поверх сгенерированного кода:
// src/api/posts.ts — ваш код поверх сгенерированного
import { useListPosts as _useListPosts } from './generated';
export function useListPosts(page = 1) {
return _useListPosts({ page, limit: 20 }, {
staleTime: 5 * 60 * 1000,
select: (data) => data.data, // убираем обёртку
});
}
3. Используйте operationId
# ❌ Без operationId — некрасивые имена функций
paths:
/posts:
get:
summary: Get posts
# ✅ С operationId — читаемые имена
paths:
/posts:
get:
operationId: listPosts # → useListPosts(), list_posts(), ListPosts()
summary: Get posts
4. Версионируйте спецификацию
# Структура
api/
├── openapi.yaml # текущая версия
├── openapi.v1.yaml # предыдущая (для обратной совместимости)
└── changelog.md # что изменилось
5. Тестируйте совместимость
# openapi-diff — проверка breaking changes
npx openapi-diff openapi.v1.yaml openapi.yaml
# Пример вывода:
# BREAKING: Removed path /posts/{id}/comments
# BREAKING: Changed type of Post.id from integer to string
# NON-BREAKING: Added optional field Post.tags
12. Сравнительная таблица инструментов
| Инструмент | Языки | Тип вывода | Бандл | React Query | Рекомендация |
|---|---|---|---|---|---|
| openapi-typescript | TypeScript | Только типы | ~6 KB | Нет (через openapi-fetch) | TS: типы + лёгкий клиент |
| Orval | TypeScript | Хуки + типы + моки | Средний | ✅ Из коробки | React/Vue + TanStack Query |
| openapi-generator | 50+ языков | SDK (классы) | Большой | Нет | Мультиязычные проекты |
| swagger-codegen | 40+ языков | SDK (классы) | Большой | Нет | Legacy Swagger 2.0 |
| oapi-codegen | Go | Типы + клиент | Минимальный | N/A | Go-проекты |
| openapi-python-client | Python | Клиент (async) | Средний | N/A | Python (идиоматичный) |
TypeScript (типы) → openapi-typescript + openapi-fetch
TypeScript (React Query) → Orval
Python → openapi-python-client
Go → oapi-codegen
Java / C# / Kotlin / 50+ языков → openapi-generator
Full-stack TS без OpenAPI → tRPC
FAQ
Что такое кодогенерация из OpenAPI?
Автоматическое создание клиентского кода (SDK), типов, серверных заглушек и моков из OpenAPI-спецификации. Вместо ручного написания HTTP-запросов вы получаете готовый типизированный клиент для любого языка.
Какой инструмент кодогенерации лучше для TypeScript?
openapi-typescript + openapi-fetch — для максимально лёгкого type-safe клиента (~6 KB). Orval — если нужны React Query хуки, Zod-валидация и MSW-моки из коробки. openapi-generator — для полноценного SDK с классами.
Можно ли генерировать код из Swagger 2.0?
Да. openapi-generator и swagger-codegen поддерживают обе версии. Рекомендуется мигрировать на OpenAPI 3.x для современных возможностей (oneOf, callbacks, links). Конвертация: swagger2openapi.
Стоит ли коммитить сгенерированный код?
Зависит от проекта. Коммитить — если важна воспроизводимость и не все разработчики знают про кодогенерацию. Генерировать при сборке — если CI/CD настроен и спецификация всегда доступна. Рекомендуемый подход — генерировать в prebuild скрипте.
Чем кодогенерация отличается от tRPC?
tRPC использует TypeScript inference — типы передаются напрямую, без спецификации и генерации. Кодогенерация требует OpenAPI-файл, но работает с любыми языками (не только TypeScript). tRPC — для монорепо, кодогенерация — для мультиязычных проектов.
Как обрабатывать аутентификацию в сгенерированном клиенте?
Большинство генераторов поддерживают securitySchemes из OpenAPI. Для openapi-fetch — передайте заголовок в конфигурации. Для openapi-generator — используйте Configuration с accessToken или apiKey. Для Orval — настройте mutator с кастомным fetch.
Полезные ссылки
- Swagger и OpenAPI — основы OpenAPI-спецификации
- Что такое REST API — REST, для которого генерируем клиенты
- tRPC — альтернатива: type-safe API без кодогенерации
- Обратная совместимость — как не сломать клиентов при изменении API
- Формат JSON — формат данных в API
- Инструменты тестирования API — тестирование сгенерированных клиентов
🚀 Создавайте API со спецификацией
LightBox API — моковые API с автоматической OpenAPI-документацией. Генерируйте типизированные клиенты для вашего фронтенда.
- ✓ OpenAPI 3.0 спецификация для каждого проекта
- ✓ Настраиваемые HTTP-статусы и ответы
- ✓ JSON-ответы любой структуры
- ✓ Логирование всех запросов
- ✓ Бесплатный план
Статья опубликована: 23 февраля 2026
Автор: LightBox API Team