GraphQL vs REST: когда использовать, trade-offs, N+1 problem
API без документации — источник фрустрации для разработчиков и барьер для интеграции. Устаревшая документация в Confluence или Google Docs быстро расходится с реальностью кода. OpenAPI Specification (ранее Swagger) решает эту проблему через machine-readable описание API, из которого автоматически генерируются интерактивные документы, клиентские SDK и валидаторы.
Зачем документация API
Onboarding разработчиков
Качественная документация API сокращает время интеграции с недель до часов. Разработчик видит:
- Доступные endpoints и методы
- Структуру request/response
- Примеры запросов
- Коды ошибок и их значения
- Аутентификацию и авторизацию
Интерактивная документация с try-it-out функциональностью позволяет тестировать API прямо в браузере без написания кода.
Contract-first design
OpenAPI Specification как контракт между frontend и backend:
- Команды договариваются об API через OpenAPI spec
- Frontend разрабатывает UI на основе mock-серверов
- Backend реализует API согласно спецификации
- Автоматические тесты проверяют соответствие контракту
Contract-first подход предотвращает breaking changes и рассинхронизацию команд.
Автоматическая генерация клиентов
Из OpenAPI spec генерируются типизированные клиенты для любого языка:
openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-axios \
-o ./generated-client
Клиент содержит типы, методы и валидацию — разработчик получает autocomplete и type safety из коробки.
OpenAPI Specification: структура, paths, schemas, parameters
Базовая структура OpenAPI 3.0
openapi: 3.0.3
info:
title: User Management API
version: 1.0.0
description: API for managing users and authentication
contact:
name: API Support
email: api@example.com
servers:
- url: https://api.example.com/v1
description: Production server
- url: https://staging-api.example.com/v1
description: Staging server
paths:
/users:
get:
summary: List all users
operationId: listUsers
tags:
- Users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
components:
schemas:
User:
type: object
required:
- id
- email
- name
properties:
id:
type: string
format: uuid
example: "123e4567-e89b-12d3-a456-426614174000"
email:
type: string
format: email
example: "user@example.com"
name:
type: string
example: "John Doe"
createdAt:
type: string
format: date-time
Pagination:
type: object
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
Описание POST endpoint с request body
paths:
/users:
post:
summary: Create a new user
operationId: createUser
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
- name
- password
properties:
email:
type: string
format: email
name:
type: string
minLength: 2
maxLength: 100
password:
type: string
format: password
minLength: 8
example:
email: "newuser@example.com"
name: "Jane Smith"
password: "SecurePass123!"
responses:
'201':
description: User created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Invalid input
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: User already exists
components:
schemas:
Error:
type: object
properties:
error:
type: string
message:
type: string
details:
type: array
items:
type: object
Аутентификация и security schemes
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
apiKey:
type: apiKey
in: header
name: X-API-Key
security:
- bearerAuth: []
paths:
/users/me:
get:
summary: Get current user profile
security:
- bearerAuth: []
responses:
'200':
description: User profile
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'401':
description: Unauthorized
Auto-generation из кода: swagger-jsdoc, decorators в NestJS
swagger-jsdoc в Express
Генерация OpenAPI spec из JSDoc комментариев:
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'User API',
version: '1.0.0',
},
servers: [
{
url: 'http://localhost:3000/api',
},
],
},
apis: ['./routes/*.js', './models/*.js'],
};
const swaggerSpec = swaggerJsdoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
/**
* @openapi
* /users:
* get:
* summary: Get all users
* tags:
* - Users
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* description: Page number
* responses:
* 200:
* description: List of users
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/User'
*/
app.get('/api/users', async (req, res) => {
const users = await User.findAll();
res.json(users);
});
/**
* @openapi
* components:
* schemas:
* User:
* type: object
* required:
* - email
* - name
* properties:
* id:
* type: string
* format: uuid
* email:
* type: string
* format: email
* name:
* type: string
*/
Decorators в NestJS
NestJS автоматически генерирует OpenAPI spec из TypeScript decorators:
import { Controller, Get, Post, Body, Query } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger';
class CreateUserDto {
@ApiProperty({ example: 'user@example.com' })
email: string;
@ApiProperty({ example: 'John Doe', minLength: 2 })
name: string;
@ApiProperty({ example: 'SecurePass123!', minLength: 8 })
password: string;
}
class UserResponseDto {
@ApiProperty({ format: 'uuid' })
id: string;
@ApiProperty()
email: string;
@ApiProperty()
name: string;
@ApiProperty({ format: 'date-time' })
createdAt: Date;
}
@ApiTags('users')
@Controller('users')
export class UsersController {
@Get()
@ApiOperation({ summary: 'Get all users' })
@ApiQuery({ name: 'page', required: false, type: Number })
@ApiQuery({ name: 'limit', required: false, type: Number })
@ApiResponse({ status: 200, type: [UserResponseDto] })
async findAll(
@Query('page') page: number = 1,
@Query('limit') limit: number = 20,
): Promise<UserResponseDto[]> {
return this.usersService.findAll(page, limit);
}
@Post()
@ApiOperation({ summary: 'Create a new user' })
@ApiResponse({ status: 201, type: UserResponseDto })
@ApiResponse({ status: 400, description: 'Invalid input' })
async create(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
return this.usersService.create(createUserDto);
}
}
Настройка Swagger в main.ts:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('User Management API')
.setDescription('API for managing users')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api-docs', app, document);
await app.listen(3000);
}
bootstrap();
Interactive docs: Swagger UI, ReDoc, try-it-out функциональность
Swagger UI
Swagger UI — интерактивная документация с возможностью тестирования API:
const swaggerUi = require('swagger-ui-express');
const YAML = require('yamljs');
const swaggerDocument = YAML.load('./openapi.yaml');
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: 'User API Documentation',
}));
Swagger UI предоставляет:
- Список всех endpoints с группировкой по tags
- Детальное описание параметров и схем
- Try-it-out кнопку для выполнения запросов
- Примеры request/response
- Авторизацию через UI (Bearer token, API key)
ReDoc — альтернативный рендерер
ReDoc — более чистый и читаемый UI для документации:
const redoc = require('redoc-express');
app.get('/docs', redoc({
title: 'User API Documentation',
specUrl: '/openapi.yaml',
redocOptions: {
theme: {
colors: {
primary: {
main: '#3498db'
}
}
}
}
}));
ReDoc лучше подходит для публичной документации (без try-it-out), Swagger UI — для внутренней разработки.
Кастомизация Swagger UI
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
explorer: true,
customCss: `
.swagger-ui .topbar { background-color: #2c3e50; }
.swagger-ui .info .title { color: #3498db; }
`,
customSiteTitle: 'My API Docs',
customfavIcon: '/favicon.ico',
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
filter: true,
tryItOutEnabled: true,
}
}));
Versioning документации: отдельные спецификации для v1/v2, changelog
Структура для нескольких версий
docs/
├── openapi-v1.yaml
├── openapi-v2.yaml
└── changelog.md
routes/
├── v1/
│ └── users.js
└── v2/
└── users.js
Настройка маршрутов для разных версий:
const swaggerV1 = YAML.load('./docs/openapi-v1.yaml');
const swaggerV2 = YAML.load('./docs/openapi-v2.yaml');
app.use('/api-docs/v1', swaggerUi.serve);
app.get('/api-docs/v1', swaggerUi.setup(swaggerV1, { customSiteTitle: 'API v1' }));
app.use('/api-docs/v2', swaggerUi.serveFiles(swaggerV2));
app.get('/api-docs/v2', swaggerUi.setup(swaggerV2, { customSiteTitle: 'API v2' }));
Changelog в OpenAPI
Документирование изменений между версиями:
info:
title: User Management API
version: 2.0.0
description: |
API for managing users and authentication.
## Changelog
### v2.0.0 (2026-05-26)
- **BREAKING**: Changed `/users` response structure
- Added pagination metadata
- Deprecated `GET /user/:id` in favor of `GET /users/:id`
- Added `PATCH /users/:id` for partial updates
### v1.0.0 (2026-01-15)
- Initial release
Deprecation warnings
paths:
/user/{id}:
get:
deprecated: true
summary: Get user by ID (deprecated)
description: |
**This endpoint is deprecated and will be removed in v3.0.0.**
Use `GET /users/{id}` instead.
parameters:
- name: id
in: path
required: true
schema:
type: string
Best practices и рекомендации
- Примеры для всех endpoints — добавляйте реалистичные примеры request/response для быстрого понимания.
- Описание ошибок — документируйте все возможные коды ошибок с примерами.
- Валидация спецификации — используйте
swagger-cli validate openapi.yamlдля проверки корректности. - CI/CD интеграция — автоматически генерируйте и публикуйте документацию при каждом деплое.
- Contract testing — используйте инструменты типа Dredd или Prism для проверки соответствия API спецификации.
- Не дублируйте информацию — используйте
$refдля переиспользования схем. - Семантическое версионирование — следуйте SemVer для версий API (major.minor.patch).
Итоги
OpenAPI Specification — стандарт документирования REST API, обеспечивающий machine-readable описание endpoints, схем и параметров. Auto-generation из кода через swagger-jsdoc или NestJS decorators гарантирует актуальность документации. Swagger UI и ReDoc предоставляют интерактивные документы с try-it-out функциональностью для быстрого тестирования. Versioning через отдельные спецификации и changelog позволяет управлять эволюцией API без breaking changes. Contract-first подход с OpenAPI ускоряет разработку, снижает количество багов и улучшает developer experience для всех потребителей API.