Я уже показывала, как получить спецификацию OpenAPI из программного кода с помощью Python-библиотеки FastAPI. Сегодня вернемся из роли разработчика в роль аналитика и составим спецификацию OpenAPI вручную на примере REST-приложения интернет-магазина, используя редактор Swagger в сервисе SwaggerHub.
Постановка задачи и проектирование REST API
Напомню, фреймворк Swagger от компании SmartBear позволяет формировать спецификацию OpenAPI 3 и интерактивно ее просматривать в визуальном веб-GUI. Спецификация OpenAPI описывает поддерживаемые REST-приложением HTTP-методы и структуры используемых данных, включая их. Редактор Swagger поддерживает работу с форматами JSON И YAML, проверяет валидность созданной спецификации и предоставляет GUI для тестирования HTTP-методов, объявленных в спецификации.
Чтобы продемонстрировать, как это работает, воспользуемся фреймворком Swagger в сервисе SwaggerHub. А в качестве примера возьмем интернет-магазин. Все пользователи (покупатель и мендежер) могут просматривать каталог товаров и поставщиков, искать товары и поставщиков определенных характеристик с применением различных фильтров, просмотреть детали конкретного товара и поставщика. Также менеджеру доступны функции добавления нового товара и поставщика, изменения этих объектов и их удаление.
Для визуализации возможностей системы интернет-магазина для пользователей составим UML-диаграмму Use Case.

Эта диаграмма создана с помощью следующего скрипта PlantUML:
@startuml
skinparam packageStyle rectangle
actor Пользователь
actor Менеджер
actor Покупатель
Пользователь <|--- Покупатель
Менеджер -|> Пользователь
rectangle Интернет_магазин {
Пользователь ---> (Посмотреть товар)
(Найти товар) <.. (Посмотреть товар) : include
(Просмотреть каталог товаров) <. (Найти товар) : include
Пользователь -> (Посмотреть поставщика)
(Найти поставщика) <.. (Посмотреть поставщика) : include
(Найти поставщика) ..> (Посмотреть список поставщиков) : include
Менеджер -> (Добавить поставщика)
Менеджер ---> (Изменить поставщика)
Менеджер -> (Удалить поставщика)
(Изменить поставщика) ..> (Посмотреть поставщика) : extend
(Удалить поставщика) ..> (Посмотреть поставщика) : extend
Менеджер --> (Добавить товар)
(Изменить товар) ...> (Посмотреть товар) : extend
(Удалить товар) ...> (Посмотреть товар) : extend
Менеджер -> (Изменить товар)
Менеджер -> (Удалить товар)
}
@enduml
Теперь начнем проектировать REST API системы интернет магазина. Сперва сопоставим варианты использования (ВИ) системы с маршрутами и конечными точками. Напомню, маршрут (Route) — это URL-адрес, который направляет запрос к определенным конечным точкам с помощью HTTP-методов. Маршрут может иметь несколько конечных точек. Конечная точка (Endpoint) — это непосредственно обращение к маршруту конкретным HTTP-методом, чтобы выполнить определенную задачу и вернуть данные с сервера клиенту. Сделаем сопоставление ВИ с маршрутами и конечными точками в таблице.
| Актор | Use Case (ВИ) | Маршрут | Конечная точка (HTTP-метод) |
| Пользователь
(Менеджер, Покупатель) |
UC-1. Посмотреть каталог товаров | /product | GET |
| UC-2. Найти товар | /product | GET с параметрами фильтрации | |
| Менеджер | UC-3. Добавить товар | /product | POST |
| Пользователь
(Менеджер, Покупатель) |
UC-4. Посмотреть товар | /product/{id} | GET |
| Менеджер | UC-6. Изменить товар | /product/{id} | PUT |
| Менеджер | UC-5. Удалить товар | /product/{id} | DELETE |
| Пользователь
(Менеджер, Покупатель) |
UC-7. Посмотреть список поставщиков | /provider | GET |
| UC-8. Найти поставщика | /provider | GET с параметрами фильтрации | |
| Менеджер | UC-10. Добавить поставщика | /provider | POST |
| Пользователь
(Менеджер, Покупатель) |
UC-9. Посмотреть поставщика | /provider/{id} | GET |
| Менеджер | UC-11. Изменить поставщика | /provider/{id} | PUT |
| Менеджер | UC-12. Удалить поставщика | /provider/{id} | DELETE |
Наконец, составим структуру данных, которая будет отражать сведения о товаре (Product) и поставщике (Provider). Сперва определимся с полями этих сущностей и их типами данных:
| Сущность | Поле | Смысл поля | Тип данных |
| Product (Товар) | id | Идентификатор товара | integer |
| name | Название товара | string | |
| category | Категория товара | string | |
| provider | Поставщик товара | object | |
| price | Стоимость товара | number | |
| quantity | Количество единиц товара | integer | |
| Provider (Поставщик) | id | Идентификатор поставщика | integer |
| name | Название поставщика | string | |
| INN | ИНН поставщика | string | |
| site | Сайт поставщика | string | |
| phone | Телефон поставщика | string | |
| address | Адрес поставщика | string |
Теперь предварительное проектирование REST API выполнено, можно переходить к формированию спецификации, что и рассмотрим далее.
Разработка спецификации OpenAPI
Формировать спецификацию будем с помощью редактора Swagger в веб-сервисе SwaggerHub, который доступен для бесплатного использования, но требует предварительной регистрации. Можно войти через сторонние сервисы, например, через Githib. После входа необходимо создать новый проект, выбрав наиболее подходящий шаблон (Template). В большинстве случае подойдет простой API (Simple API).

Заготовка спецификации сформируется автоматически в формате YAML и все, что нужно сделать – это исправить ее по своему проекту. Для этого надо внести своих акторов, свои HTTP-методы и структуры данных.

В случае моего кейса пример JSON-структуры данных, которая отражает сведения о товаре, выглядит так:
{
"id": 1,
"name": "яблоки",
"category": "еда",
"provider": {
"id": 1,
"name": "ООО Ромашка",
"INN": "1234567890",
"site": "https://www.ooo-camomile.com",
"phone": "7-495-123-45-67",
"address": "г. Москва, ул. Ленина, 123"
},
"price": 145,
"quantity": 10
}
А пример структуры данных, которая отражает сведения о поставщике, также представленный в формате JSON, будет таким:
{
"id": 1,
"name": "ООО Ромашка",
"INN": "1234567890",
"site": "https://www.ooo-camomile.com",
"phone": "7-495-123-45-67",
"address": "г. Москва, ул. Ленина, 123"
}
Теперь составим саму спецификацию OpenAPI в формате YAML, который немного отличается от JSON, о чем я подробнее писала здесь. Итоговая спецификация выглядит следующим образом:
openapi: 3.0.0
servers:
# Added by API Auto Mocking Plugin
- description: SwaggerHub API Auto Mocking
url: https://virtserver.swaggerhub.com/VICHIGOVAANNA/Internet-shop/1.0.0
info:
description: 'Типичный интернет-магазин - демо-кейс Анны Вичуговой'
version: "1.0.0"
title: 'API интернет-магазина'
contact:
email: anna@mail.com
tags:
- name: manager
description: 'Менеджер'
- name: customer
description: 'Покупатель'
paths:
/product:
get:
tags:
- manager
- customer
summary: 'Посмотреть каталог товаров'
operationId: viewProductCatalog
description: 'Параметры фильтрации товаров в каталоге для поиска'
parameters:
- in: query
name: name
description: 'название товара для поиска'
required: false
schema:
type: string
- in: query
name: category
description: 'категория товара для поиска'
required: false
schema:
type: string
- in: query
name: provider
description: 'поставщик товара для поиска'
required: false
schema:
type: string
- in: query
name: min_price
description: 'минимальная цена товара для поиска'
schema:
type: integer
format: int32
minimum: 0
maximum: 100500
- in: query
name: max_price
description: 'максимальная цена товара для поиска'
schema:
type: integer
format: int32
minimum: 0
maximum: 10005000
responses:
'200':
description: 'результаты поиска по запросу'
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Product'
'400':
description: 'не верные параметры фильтрации'
post:
tags:
- manager
summary: 'Добавить товар'
operationId: addProduct
description: 'Добавить новый товар в каталог'
responses:
'201':
description: 'товар добавлен'
content:
application/json:
schema:
type: object
properties:
id:
type: integer
example: 1
product:
$ref: '#/components/schemas/Product'
'400':
description: 'некорректный ввод'
'409':
description: 'такой товар уже есть'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
description: 'Новый товар для добавления в каталог'
/provider:
get:
tags:
- manager
- customer
summary: 'Посмотреть список поставщиков'
operationId: viewProviderCatalog
description: 'Параметры фильтрации поставщиков в каталоге для поиска'
parameters:
- in: query
name: name
description: 'название поставщика для поиска'
required: false
schema:
type: string
- in: query
name: INN
description: 'ИНН поставщика для поиска'
required: false
schema:
type: string
- in: query
name: phone
description: 'телефон поставщика для поиска'
required: false
schema:
type: string
- in: query
name: address
description: 'адрес поставщика для поиска'
required: false
schema:
type: string
responses:
'200':
description: 'результаты поиска по запросу'
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Provider'
'400':
description: 'не верные параметры фильтрации'
post:
tags:
- manager
summary: 'Добавить поставщика'
operationId: addProvider
description: 'Добавление нового поставщика в каталог'
responses:
'201':
description: 'поставщик добавлен'
content:
application/json:
schema:
type: object
properties:
id:
type: integer
example: 1
product:
$ref: '#/components/schemas/Provider'
'400':
description: 'некорректный ввод'
'409':
description: 'такой поставщик уже есть'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Provider'
description: 'Новый поставщик для добавления в каталог'
/product/{id}:
get:
tags:
- manager
- customer
summary: 'Посмотреть товар'
operationId: viewProduct
description: 'Просмотр данных о конкретном товаре по его ID'
parameters:
- in: path
name: id
description: ID
required: true
schema:
type: integer
format: int64
example: 1
responses:
'200':
description: 'Товар найден'
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'404':
description: 'Товар не найден'
put:
tags:
- manager
summary: 'Изменить товар'
operationId: updateProduct
description: 'Изменение параметров товара'
parameters:
- name: id
in: path
description: 'ID товара, параметры которого нужно изменить'
required: true
schema:
type: integer
example: 1
responses:
'200':
description: 'параметры товара изменены успешно'
'400':
description: 'некорректный ввод'
'404':
description: 'товар не найден'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
description: 'Измененные параметры товара'
delete:
tags:
- manager
summary: 'Удалить товар'
operationId: deleteProduct
description: 'Удаление товара по его идентификатору'
parameters:
- name: id
in: path
description: 'Идентификатор товара для удаления'
required: true
schema:
type: integer
example: 1
responses:
'200':
description: 'товар успешно удален'
'404':
description: 'товар не найден'
'500':
description: 'внутренняя ошибка сервера'
/provider/{id}:
get:
tags:
- manager
- customer
summary: 'Посмотреть поставщика'
operationId: viewProvider
description: 'Просмотр информации о конкретном поставщике по его ID'
parameters:
- in: path
name: id
description: Идентификатор поставщика
required: true
schema:
type: integer
format: int64
example: 1
responses:
'200':
description: 'Поставщик найден'
content:
application/json:
schema:
$ref: '#/components/schemas/Provider'
'404':
description: 'Поставщик не найден'
put:
tags:
- manager
summary: 'Изменить поставщика'
operationId: updateProvider
description: 'Изменение поставщика по его идентификатору'
parameters:
- name: id
in: path
description: 'ID поставщика, параметры которого нужно изменить'
required: true
schema:
type: integer
example: 1
responses:
'200':
description: 'параметры поставщика изменены успешно'
'400':
description: 'некорректный ввод'
'404':
description: 'товар не найден'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Provider'
description: 'Измененные параметры поставщика'
delete:
tags:
- manager
summary: 'Удалить поставщика'
operationId: deleteProvider
description: 'Удаление поставщика по его идентификатору'
parameters:
- name: id
in: path
description: 'Идентификатор поставщика для удаления'
required: true
schema:
type: integer
example: 1
responses:
'200':
description: 'поставщика успешно удален'
'404':
description: 'поставщика не найден'
'500':
description: 'внутренняя ошибка сервера'
components:
schemas:
Product:
type: object
required:
- name
- category
- provider
- price
- quantity
properties:
id:
type: integer
example: 1
name:
type: string
example: 'яблоки'
category:
type: string
example: 'еда'
provider:
$ref: '#/components/schemas/Provider'
price:
type: number
example: 145.00
quantity:
type: integer
example: 10
Provider:
required:
- name
- INN
- phone
- address
properties:
id:
type: integer
example: 1
name:
type: string
example: 'ООО Ромашка'
INN:
type: string
example: 1234567890
site:
type: string
format: url
example: 'https://www.ooo-camomile.com'
phone:
type: string
example: 7-495-123-45-67
address:
type: string
example: 'г. Москва, ул. Ленина, 123'
Помимо ранее показанных маршрутов и конечных точек с HTTP-методами со структурами данных и примерами их наполнения, в этой спецификации также показаны HTTP-ответы на запросы, подробнее о которых я писала здесь. Редактор Swagger сразу отображает UI для тестирования описанного REST API.

Поскольку при создании проекта я задала для него публичную видимость, сгенерированная документация доступна по ссылке https://app.swaggerhub.com/apis-docs/VICHIGOVAANNA/Internet-shop/1.0.0
Надеюсь, что это небольшое руководство поможет аналитикам, которые только начинают знакомиться с REST API и Swagger, лучше понять их устройство и принципы работы. Более сложную версию этой документации OpenAPI со схемой безопасности на базе JWT-токена аутентификации пользователей, а также пошаговый алгоритм разработки спецификации я привожу в новой статье. А о том, какие ошибки чаще всего встречаются при разработке спецификации OpenAPI и как их избежать, читайте здесь.
А подробнее познакомиться со всеми рассмотренными темами, а также другими основами архитектуры и интеграции информационных систем вам помогут курсы Школы прикладного бизнес-анализа в нашем лицензированном учебном центре обучения и повышения квалификации системных и бизнес-аналитиков в Москве:


