Пошук уроків, статей та іншого контенту
Опишете REST API за допомогою OpenAPI та згенеруєте документацію, зрозумілу розробникам і споживачам API.
OpenAPI — це формальний опис HTTP API у форматі YAML або JSON. Специфікація визначає:
доступні маршрути;
HTTP-методи;
параметри запитів;
структуру тіла запиту;
можливі відповіді;
схеми даних;
помилки;
механізми автентифікації;
приклади запитів і відповідей.
OpenAPI-файл є контрактом між сервером і споживачами API. За ним можна:
згенерувати інтерактивну документацію;
перевірити коректність специфікації;
згенерувати клієнтський код;
створити серверні заготовки;
автоматизувати перевірку сумісності змін.
OpenAPI описує API, але не реалізує його. Реалізація маршрутів у Node.js і специфікація OpenAPI мають відповідати одна одній.
Мінімальний OpenAPI-документ містить поля openapi, info і paths:
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
description: API для роботи з користувачами
paths:
/users/{id}:
get:
summary: Отримати користувача
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Користувача знайдено
'404':
description: Користувача не знайденоinfoПоле info описує сам API:
title — назва;
version — версія контракту;
description — призначення API;
contact — контактна інформація команди;
license — ліцензія, якщо вона потрібна.
Версія в info.version — це версія документації або контракту. Вона не обов’язково має збігатися з версією Node.js чи версією окремого npm-пакета.
serversЗа допомогою servers можна описати базові адреси API:
servers:
- url: https://api.example.com/v1
description: Production
- url: http://localhost:3000/api
description: Local developmentЯкщо маршрут описано як /users, то повна адреса буде:
https://api.example.com/v1/usersОпис середовищ допомагає споживачам перемикатися між локальним, тестовим і робочим API.
pathsУ paths описуються ресурси та операції над ними:
paths:
/users:
get:
summary: Отримати список користувачів
post:
summary: Створити користувача
/users/{id}:
get:
summary: Отримати одного користувачаКлюч маршруту не повинен містити базовий URL або назву середовища. Базовий URL задається через servers.
Кожен HTTP-метод є окремою операцією:
paths:
/users/{id}:
get:
operationId: getUserById
summary: Отримати користувача
description: Повертає повну інформацію про користувача за його ідентифікатором.Корисні поля операції:
operationId — стабільне унікальне ім’я операції;
summary — короткий заголовок;
description — детальний опис;
tags — групування операцій у документації;
parameters — параметри;
requestBody — тіло запиту;
responses — можливі відповіді;
deprecated — позначка застарілої операції;
security — вимоги до автентифікації.
operationId особливо важливий, якщо з OpenAPI генерується клієнтський код. Його слід робити унікальним і стабільним: зміна цього поля може змінити назву методу в згенерованому клієнті.
OpenAPI розрізняє параметри за розташуванням:
path — частина URL;
query — параметри після ?;
header — HTTP-заголовки;
cookie — cookie.
Path-параметр завжди має бути обов’язковим:
parameters:
- name: id
in: path
required: true
description: Числовий ідентифікатор користувача
schema:
type: integer
format: int64
minimum: 1Для маршруту /users/{id} має існувати параметр з таким самим іменем — id.
parameters:
- name: limit
in: query
required: false
description: Максимальна кількість записів у відповіді
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: role
in: query
required: false
schema:
type: string
enum:
- admin
- userОбмеження minimum, maximum, enum і default не замінюють перевірку в сервері. Вони описують очікуваний контракт і можуть використовуватися інструментами для валідації або генерації клієнтів.
Для JSON-тіла використовується requestBody:
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'Можна вказати приклад:
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
example:
name: Олена Коваль
email: olena@example.comschema описує форму даних, а example показує конкретне значення, яке побачить споживач API в документації.
Кожна операція повинна мати responses. Для кожного статусу вказується пояснення і, за потреби, тіло відповіді:
responses:
'200':
description: Користувача знайдено
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: Користувача не знайдено
content:
application/json:
schema:
$ref: '#/components/schemas/Error'Документуйте всі очікувані категорії результатів:
успішну відповідь;
помилку валідації;
відсутній ресурс;
помилку автентифікації або авторизації;
конфлікт;
внутрішню помилку сервера.
Не варто описувати лише 200. Споживач API повинен розуміти, як обробляти помилки.
componentsСпільні схеми зберігають у components.schemas:
components:
schemas:
User:
type: object
required:
- id
- name
- email
properties:
id:
type: integer
format: int64
readOnly: true
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
format: email
Error:
type: object
required:
- message
properties:
message:
type: stringПосилання на схему створюється через $ref:
schema:
$ref: '#/components/schemas/User'Переваги такого підходу:
одна структура описується в одному місці;
документація не дублює великі схеми;
зміни легше підтримувати;
інструменти можуть коректно генерувати типи.
readOnly: true означає, що поле може повертатися сервером, але не повинно передаватися клієнтом під час створення ресурсу.
Для запиту створення користувача можна описати окрему схему:
CreateUserRequest:
type: object
required:
- name
- email
properties:
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
format: emailРозділяйте схеми запиту і відповіді, якщо їхні поля або правила відрізняються.
Створіть файл openapi.yaml:
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
description: REST API для перегляду та створення користувачів
servers:
- url: http://localhost:3000/api
description: Локальне середовище
tags:
- name: Users
description: Операції з користувачами
paths:
/users:
get:
tags:
- Users
operationId: listUsers
summary: Отримати список користувачів
parameters:
- name: limit
in: query
required: false
description: Максимальна кількість користувачів
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
'200':
description: Список користувачів
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
example:
- id: 1
name: Олена Коваль
email: olena@example.com
post:
tags:
- Users
operationId: createUser
summary: Створити користувача
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
example:
name: Андрій Мельник
email: andrii@example.com
responses:
'201':
description: Користувача створено
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Некоректні дані запиту
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/users/{id}:
get:
tags:
- Users
operationId: getUserById
summary: Отримати користувача за ідентифікатором
parameters:
- name: id
in: path
required: true
description: Ідентифікатор користувача
schema:
type: integer
format: int64
minimum: 1
responses:
'200':
description: Користувача знайдено
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: Користувача не знайдено
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
User:
type: object
required:
- id
- name
- email
properties:
id:
type: integer
format: int64
readOnly: true
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
format: email
CreateUserRequest:
type: object
required:
- name
- email
properties:
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
format: email
Error:
type: object
required:
- message
properties:
message:
type: stringДля відображення інтерактивної документації можна використати swagger-ui-express. Він читає OpenAPI-документ і створює вебсторінку з описом маршрутів та кнопкою для виконання запитів.
Встановіть залежності:
npm init -y
npm install express swagger-ui-express yamlСтворіть файл server.js:
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const YAML = require('yaml');
const fs = require('node:fs');
const app = express();
const port = 3000;
const openapiDocument = YAML.parse(
fs.readFileSync('./openapi.yaml', 'utf8')
);
app.use(express.json());
const users = [
{
id: 1,
name: 'Олена Коваль',
email: 'olena@example.com'
}
];
app.get('/api/users', (req, res) => {
const requestedLimit = Number.parseInt(req.query.limit, 10);
const limit = Number.isInteger(requestedLimit)
? Math.min(Math.max(requestedLimit, 1), 100)
: 20;
res.json(users.slice(0, limit));
});
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (
typeof name !== 'string' ||
name.trim().length === 0 ||
typeof email !== 'string' ||
!email.includes('@')
) {
return res.status(400).json({
message: 'Поля name та email мають містити коректні значення'
});
}
const user = {
id: users.length + 1,
name: name.trim(),
email: email.trim()
};
users.push(user);
return res.status(201).json(user);
});
app.get('/api/users/:id', (req, res) => {
const id = Number.parseInt(req.params.id, 10);
const user = users.find((item) => item.id === id);
if (!user) {
return res.status(404).json({
message: 'Користувача не знайдено'
});
}
return res.json(user);
});
// Публікуємо інтерактивну документацію за адресою /docs.
app.use('/docs', swaggerUi.serve, swaggerUi.setup(openapiDocument));
// Публікуємо сирий OpenAPI-документ для інструментів і клієнтів.
app.get('/openapi.json', (req, res) => {
res.json(openapiDocument);
});
app.listen(port, () => {
console.log(`API запущено на http://localhost:${port}`);
console.log(`Документація доступна на http://localhost:${port}/docs`);
});Запустіть сервер:
node server.jsПісля запуску доступні такі адреси:
http://localhost:3000/api/users — API;
http://localhost:3000/docs — інтерактивна документація;
http://localhost:3000/openapi.json — OpenAPI-документ у JSON.
Swagger UI відобразить маршрути, параметри, схеми, приклади та можливість виконати запит безпосередньо зі сторінки документації.
У прикладі специфікація і сервер мають однаковий базовий шлях:
/api/usersЦе важливо, оскільки значення servers.url не змінює маршрути Express. Воно лише описує, де споживач повинен їх шукати.
Потрібно синхронізувати:
шлях і HTTP-метод;
назви path-параметрів;
обов’язковість полів;
формати даних;
статуси відповідей;
Content-Type;
форму помилок;
обмеження значень.
Наприклад, якщо OpenAPI описує відповідь як масив User, сервер не повинен повертати об’єкт виду:
{
"items": []
}без зміни специфікації. Така невідповідність вводить споживачів в оману і може зламати згенерований клієнт.
tags групують операції у Swagger UI:
tags:
- name: Users
description: Операції з користувачамиНа рівні операції тег використовується так:
get:
tags:
- UsersДля складних операцій додавайте description. У ньому варто пояснювати:
бізнес-умови;
значення полів;
поведінку за відсутності результатів;
правила сортування або фільтрації;
особливості помилок.
summary має бути коротким і зрозумілим, наприклад:
summary: Отримати список користувачівНе варто використовувати в summary внутрішні назви функцій або контролерів.
Якщо API використовує Bearer-токени, схему можна описати в components.securitySchemes:
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWTПісля цього захист можна застосувати до всієї специфікації:
security:
- bearerAuth: []Або лише до конкретної операції:
paths:
/users:
get:
security:
- bearerAuth: []Документуйте автентифікацію лише тоді, коли сервер справді її підтримує. OpenAPI-опис сам по собі не додає перевірку токена до Express.
Версію API можна закладати в URL:
servers:
- url: https://api.example.com/v1Або підтримувати кілька специфікацій для різних версій. У будь-якому випадку зміни потрібно класифікувати:
додавання нового необов’язкового поля зазвичай є сумісною зміною;
додавання нового маршруту зазвичай є сумісною зміною;
перейменування поля є потенційно несумісною зміною;
видалення поля або статусу відповіді є несумісною зміною;
зміна типу поля є несумісною зміною.
Для застарілих операцій використовуйте:
deprecated: trueЦе сигналізує споживачам, що операцію потрібно поступово замінити.
Перед публікацією специфікацію потрібно перевіряти. Валідація допомагає знайти:
некоректний YAML;
відсутні обов’язкові поля;
неправильні $ref;
path-параметри без відповідних оголошень;
некоректні типи;
дублікати operationId.
Перевіряйте специфікацію в CI, а не лише вручну перед релізом. Документ, який не проходить валідацію, не повинен потрапляти в основну гілку або публічну документацію.
Окремо корисно перевіряти відповідність фактичних HTTP-відповідей описаним схемам. Синтаксично правильний OpenAPI-файл ще не гарантує, що сервер повертає саме такі дані.
Практичний процес розробки може виглядати так:
Визначити ресурси, операції та формати даних.
Описати контракт в openapi.yaml.
Перевірити специфікацію валідатором.
Підключити Swagger UI до Node.js-сервера.
Реалізувати маршрути відповідно до контракту.
Перевірити приклади запитів і відповідей.
Додати перевірку специфікації до CI.
Оновлювати документацію разом зі змінами API.
Для вже наявного API важливо спочатку зафіксувати фактичну поведінку, а потім поступово покращувати контракт. Інакше документація може описувати бажану, але не реальну поведінку сервера.
Для /users/{id} параметр повинен мати:
in: path
required: trueЯкщо id описано як query-параметр, специфікація не відповідає маршруту.
Опис лише успішної відповіді не пояснює споживачу, що робити зі статусами 400, 404 або 500.
Копіювання однакової структури в кожну операцію призводить до розбіжностей. Спільні моделі потрібно виносити в components.schemas.
Опис id як просто string або object приховує важливі правила API. Використовуйте точні типи, format, обмеження довжини, діапазони та enum, коли вони справді підтримуються сервером.
servers.url і серверних маршрутівЯкщо в OpenAPI вказано /api, а Express слухає /users, інтерактивна кнопка в документації надсилатиме запити не за тією адресою.
Приклади повинні містити реальні значення правильного типу. Якщо приклад не проходить серверну валідацію, він знижує довіру до документації.
operationIdАвтоматично згенеровані або випадкові ідентифікатори ускладнюють підтримку клієнтського коду. Задавайте operationId явно.
Ручне редагування YAML легко призводить до помилок. Додавайте перевірку синтаксису та структури OpenAPI до процесу інтеграції.
OpenAPI є контрактом REST API у форматі YAML або JSON.
У paths описуються маршрути та HTTP-операції.
parameters описують path-, query-, header- і cookie-параметри.
requestBody описує тіло запиту.
responses документують успішні та помилкові результати.
Повторно використовувані моделі зберігаються в components.schemas.
operationId потрібен для стабільної ідентифікації операцій.
swagger-ui-express дозволяє показати інтерактивну документацію в Node.js.
OpenAPI не замінює реалізацію та валідацію серверних даних.
Специфікацію потрібно перевіряти й оновлювати разом із кодом API.