Привет, Хабр! Меня зовут Виктория Юльская, и я старший системный аналитик в Ozon.
Я думаю, здесь найдётся много людей, которые хоть раз работали с документацией API в Confluence. Да-да, те самые километровые страницы на каждый метод — с описанием всего и вся в виде текста, таблиц, диаграмм последовательности и т. д.

Зачастую такая документация API в Confluence устаревает ровно в тот момент, как её закончили писать. После передачи задачи в разработку, как только что-то непонятно, куд�� все идут? Правильно, к аналитику — «А как это работает? А что это значит? А что если...?».
Ну вот же дока, там всё написано... но обычно никто не хочет читать огромную доку на метод, быстрее же спросить. И зачастую у самих аналитиков есть вопросики по актуальности этой документации (уже есть новые договорённости со встреч, комментарии в документации и т. д.).
Есть ли более эффективный способ ведения и поддержания документации API в актуальном состоянии? Давайте разбираться.
Немного вводной части
Сгенерировать спецификацию/документацию можно из аннотаций в коде, и многие думают, что генерация спецификации из кода приводит к меньшему отклонению документации, т.к. она тесно связана с кодом. Но всё-таки считается, что это не лучший подход.
То, о чём мы сегодня с вами поговорим, наилучшим образом ложится на подход Specification-first / Manifest First / Design API First — называйте как хотите, но суть одна — сначала спецификация, потом код!
Эта тема достаточно холиварная и зачастую сталкивает лбами системных аналитиков и разработчиков :) Тем не менее подход не новый и давно уже показал себя как вполне себе эффективный.

Не будем сильно погружаться в сравнение подходов и как сделать правильный выбор, но в двух словах подсветим преимущества подхода Design API First:
Согласованный контракт до разработки
Во-первых, вы можете спастись от многих ошибок, выявив их на этапе проектирования. Во-вторых, у вас, помимо классно работающего API, появляется документация, которая даст пользователям понимание, как пользоваться вашим API.Параллельная разработка
Спроектировав заранее контракт API, команды тестирования, разработки клиента API и разработки сервиса, реализующего API, могут работать параллельно, что, в свою очередь, приводит к увеличению производительности команд.Скорость и качество тестирования
Спецификация API упрощает QA-специалистам создание тест-кейсов, что обеспечивает общее более высокое качество ПО. Также на основе спецификации OpenAPI можно генерировать API-тесты.Кодогенерация
Из готовой спецификации OpenAPI можно сгенерировать клиент, сервер, документацию (HTML, Confluence Wiki), ну и, как было сказано выше, — API-тесты, что позволит вам автоматизировать большую работу в рамках всего проекта и уменьшить объём рутинного кода.Проектирование API аналитиком и архитектором (если таковых не имеется, то с привлечением разработчиков)
Мы создаём API для потребителей, и аналитик, как никто другой, помнит об этом при проектировании удобного и понятного API с подробным описанием и примерами. Плюс к тому же, аналитик знает потребности пользователей, пользовательский путь и как работает система изнутри, что позволяет ему качественно описать необходимые методы и модели.
Сразу хочу всех успокоить: разработчиков из данного этапа никто не исключает, они неотъемлемая часть и также играют важную роль. Ну и частенько именно разработчики полностью берут на себя проектирование API, просто, скорее всего, вы в такой спецификации не увидите описания параметров и корректных понятных примеров. Но в любом случае нужно решить ряд вопросов совместно:как будут взаимодействовать аналитик, архитектор и разработчик при проектировании спецификации;
процесс ревью спецификации;
как вносить изменения в спецификацию;
как уведомлять о готовности спецификации;
где хранить спецификации и куда доставлять изменения;
поддержка моков и многое другое.
Design API First-подход в первую очередь помогает создавать API более эффективно, в связи с чем все чаще встречается именно ручная разработка спецификации. Т.к. спецификация может быть максимально полезна ещё до разработки кода — согласованные контракты с примерами, моки, тесты и т. д.
Спецификация OpenAPI
Все мы хорошо знаем, что REST — это архитектурный стиль, а не конкретный стандарт. Тем не менее есть несколько спецификаций REST, которые помогают представить стандарты в виде описания REST API.
Данную статью как раз хотелось бы посвятить одной из них — OpenAPI, пожалуй, самой популярной на данный момент спецификации.
Наиболее используемые инструменты Swagger:
Swagger UI — веб-приложение, которое позволяет визуализировать спецификацию Open API в интерактивном пользовательском интерфейсе, выполнить запрос на сервер и получить ответ;
Swagger Codegen — генерация клиентов, серверных заглушек, SDK-пакетов и документации на основе определений спецификации Open API;
Swagger Core — генерация документации на основе аннотаций в существующем коде.
Нас больше всего сейчас интересует, что использовать для разработки спецификации OpenAPI, а это:
Swagger editor — интерфейс для создания файла документации по спецификации Open API. Не очень удобен тем, что большой файл будет грузиться достаточно долго...
Любая IDE с расширением для валидации и визуализации OpenAPI-спецификации (Swagger Viewer и др.).
Конечно, можно использовать и обычный текстовый редактор, но использование инструментов, которые позволяют визуализировать вашу спецификацию, упростят проверку её валидности.
Спецификацию OpenAPI мы можем писать как на JSON, так и на YAML.
С YAML работать легче, потому что не требуется расставлять скобки и запятые, т.е. документ более понятный для человека, но есть нюанс — надо внимательнее следить за расстановкой интервалов.
Далее будем рассматривать структуру спецификации OpenAPI в обоих вариантах.
Организация файлов спецификации OpenAPI
Для того чтобы спецификацию можно было легко и приятно поддерживать, да и в целом читать, я обычно выношу описание методов и описание моделей в отдельные файлы и ссылаюсь на нужные существующие объекты с помощью указания путей до них. Можно отдельно вынести и описание параметров, описание ошибок — но это поможет, если ваши сервисы содержат очень много повторяющихся параметров и/или ошибок.
Мне приходилось писать много новых спецификаций, и скрипт для автогенерации шаблона спецификации OpenAPI очень упростил жизнь, предзаполняя за меня метаинформацию.
Относитесь к спецификации как к программному коду. Иначе в конечном итоге она превратится в мусор.
Для себя я выделила следующий паттерн в организации файлов спецификации OpenAPI:
root_folder← корень репозиторияresource_or_api_controller← папка ресурса (если делим не по тегам в одном файле спецификации, а разносим по отдельным файлам)api.yaml← файл для описания методовmodels.yaml← файл для описания моделейparameters.yaml← файл для описания параметров api-методов (опционально)errors.yaml← файл для описания ошибок, которые могут вернуть методы (опционально)
Конечно, можно всю спецификацию описать в одном файле, но если у вас большой сервис со множеством методов и большими моделями, то и спецификация будет соответствующая. Навигироваться по такому файлу будет сильно сложнее.

Базовая структура спецификации OpenAPI
Из чего состоит спецификация OpenAPI:
openapi- содержит номер версии спецификации OpenAPI.info- cодержит основную информацию о вашем API: название, описание, версию, контакты разработчика спецификации и т. д.servers- содержит информацию об используемых серверах.указывается базовый путь, который находится перед конечной точкой.
можно указать несколько вариантов - для разных сред разработки
components- в объекте components можно хранить множество различных переиспользуемых объектов. Объект components может содержать следующее: схемы, ответы, парамет��ы, примеры, тело запроса, заголовки, схемы безопасности и тд. При делении спецификации на два файла — api.yaml и models.yaml — нам данный блок нужен только для определения схемы безопасности, всё остальное уходит в файл models.yaml.security- для отправки запросов, авторизованных нашим сервером API, спецификация должна содержать информацию о безопасности, которая авторизует запрос.объявленные поля components.securitySchemes и security свидетельствуют о том, что у любого метода в этом файле должен быть установлен хедер Authorization, в нашем случае с JWT-токеном.
при этом у каждого метода можно определить секцию security:[], оставив её пустой, которая будет свидетельствовать о том, что для данного метода авторизация не нужна.
о том, как декларировать различные схемы авторизации (apiKey, http, ouath2, openIdConnect, mutualTLS), можно в подробностях почитать в официальной документации OpenAPI
paths- содержит доступные пути (конечные точки) и операции (методы) для API. Подробнее о заполнении данного блока рассмотрим в следующем разделе.tags- в данном объекте перечисляются все теги, в которые вы будете объединять свои конечные точки (по пользовательским ролям или фичам продукта).externalDocs- содержит ссылки на внешние ресурсы для получения расширенной информации.
Пример заполнения в yaml
openapi: 3.1.0
info:
title: "Myproject API"
description: “Description of the purpose of your service”
version: "1.0.0"
termsOfService: “https://myproject.ru/terms”
contact:
name: Ванька Петров
email: user@gmail.com
servers:
- url: https://dev.api.myproject.ru/api/v2
description: Test server dev
- url: https://stg.myproject.ru/api/v2
description: Test server stg
- url: https://myproject.ru/api/v2
description: Test server prod
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
paths:
/users:
get:
security:
- bearerAuth: []
tags:
- name: Profile
description: "Профиль"
- name: Products
description: "Товары"
externalDocs:
description: Find more info here
url: https://example.comПример заполнения в json
{
"openapi": "3.1.0",
"info": {
"title": "Myproject API",
"description": "Description of the purpose of your service",
"version": "1.0.0",
"termsOfService": "https://myproject.ru/terms",
"contact":{
"name": "Ванька Петров",
"email": "user@gmail.com"
}
},
"servers": [
{
"url": "https://dev.api.myproject.ru/api/v2",
"description":"Test server dev"
},
{
"url": "https://stg.myproject.ru/api/v2",
"description":"Test server stg"
},
{
"url": "https://myproject.ru/api/v2",
"description":"Test server prod"
}
],
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "jwt"
}
}
},
"paths": {
"/users": {
"get": {
"security": [
{
"bearerAuth": []
}
]
}
}
},
"tags": [
{
"name": "Profile",
"description": "Профиль"
},
{
"name": "Products",
"description": "Товары"
}
],
"externalDocs":{
"description": "Find more info here",
"url": "https://example.com"
}
}Далее чуть подробнее разберёмся с ресурсами и методами. Для общего понимания — как может выглядеть ваша спецификация на один метод (в упрощённом варианте описания):

api.yaml
info:
title: API Test
version: '1.0'
servers:
- url: https://api.server.test/v1
paths:
/feedback/{id}/report:
post:
summary: Название метода
description: Описание работы метода
security:
- bearerAuth: []
parameters:
- in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Report'
required: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/InfoMessage"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
Report:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
InfoMessage:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string Ресурсы и методы
Объект path содержит доступные пути (конечные точки) и операции (методы) для API. Он состоит из:
пути (конечной точки) — все пути в блоке
pathзадаются относительно URL, определённых в блоке «Серверы», то есть полный URL запроса будет выглядеть так:<server-url>/path.операций (методов
GET,POSTи т. д.), которые в свою очередь включают:summary— название методаdescription— описание работы метода. Описывайте задачу, которую решает метод или свойствоsecurity: []— определяет глобальный метод безопасностиparameters— параметры запросаrequestBody— тело запросаresponses— описание ответа
Есть множество других элементов, но остановимся на обязательной основе.

Правила именования пути
Есть 3 типа ресурсов:
Документ — один объект. К примеру, одно сообщение в списке (
api/messages/{id}— документ обычно вложен в коллекцию, но есть исключения).в пути используются в таком случае только существительные.
последнее существительное в единственном числе.
Коллекция — множество объектов. К примеру, список сообщений (
api/messages).в пути используются в таком случае только существительные.
последнее существительное во множественном числе.
Контроллер — действие. К примеру, оформление заказа (
/api/cart/checkout).можно использовать глаголы.
действие должно относится к чему-то (
/api/cart/checkout— checkout относится к корзине, лучше не делать просто/api/checkout— не самый лучший пример, но суть должна быть понятна).
Если вы следуете принципам REST, то в названии пути не пишем действие, о котором говорит HTTP method (create, update, delete и т. д.). Стараемся делать как можно больше документов и коллекций и как можно меньше контроллеров.
✅POST /courses — Создать новый курс
❌POST /courses/create — Создать новый курс
Но здесь надо помнить, что ваш API может быть реализован полностью на POST, и в таком случае вполне нормально использовать действия в названии пути:
✅POST /courses/create — Создать новый курс
Методы
Метод | Описание | Комментарий |
GET | Возвращает представление ресурса по указанному универсальному коду ресурса (URI). Текст ответного сообщения содержит сведения о запрашиваемом ресурсе. | GET-запрос может содержать тело запроса, но прокси могут просто отбрасывать тело GET-запроса. GET-запросы по умолчанию кешируются через URI. Если параметры передаются в теле, то кеши работать не будут. ОС может самостоятельно повторить GET-запрос. |
POST | Создаёт новый ресурс по указанному URI. Текст запроса содержит сведения о новом ресурсе. Метод POST также можно использовать для запуска операций, не относящихся непосредственно к созданию ресурсов (для операции контроллера). | |
PUT | Создаёт или заменяет ресурсы по указанному URI. В тексте сообщения запроса указан создаваемый или обновляемый ресурс. Лучше все-таки разделять создание (делать POST) и изменение (PUT / PATCH). | Полностью перезаписывает ресурс. |
PATCH | Выполняет частичное обновление ресурса. Текст запроса определяет набор изменений, применяемых к ресурсу. | Перезаписывает только определённую часть.
|
DELETE | Удаляет ресурс по указанному URI. | Не содержит тела. |
Параметры пути и запроса
Параметры пути и запроса состоят из:
name: имя параметра.in: место параметра. Возможные значения:header— параметры, включённые в заголовок запросаpath— параметры в пределах path конечной точки перед строкой запросаquery— параметры в строке запроса конечной точкиcookie— параметры в заголовке Cookie
description: описание параметра.required: обязательность параметра.schema: схема или модель для параметра. Схема определяет структуру входных или выходных данных.example: пример типа носителя. Если объект example содержит примеры, эти примеры появляются в Swagger UI, а не в содержимом объекта example.
Также параметры запроса можно выносить в models.yaml и ссылаться ($ref) на параметры из моделей. Пример:
parameters:
- $ref: "models.yaml#/components/parameters/Param1"Для добавления в компоненты параметров необходимо на уровне с элементом schema добавить элемент parameters и описать там все необходимые параметры.
Для ограничения возможных значений параметра запроса необходимо использовать ключевое слово enum.
Описание параметра запроса в models.yaml для того, чтобы ссылаться на него и переиспользовать без дублирующего описания.
parameters:
filter_type:
name: filter_type
in: query
description: |
Тип фильтра заказов пользователя:
- all — все заказы
- current — текущие
- done — выполненные
schema:
enum: ["all", "current", "done"]
type: stringПараметры пути
Для того чтобы добавить параметр в путь запроса, необходимо использовать фигурный скобки {}. Обычно это используется для указания определённого элемента в коллекции. Путь может иметь несколько параметров
GET /users/{userId}:
GET /cars/{carId}/drivers/{driverId}:
Каждый параметр пути должен быть заменён фактическим значением при вызове.
Для определения параметров пути нужно использовать следующую конструкцию in: path. Необходимо также добавить required: true, чтобы указать обязательность данного параметра.
paths:
/users/{userId}:
get:
parameters:
- name: userId # имя использовать такое же, как и в пути
in: path
description: Идентификатор пользователя
required: true # обязательный параметр
schema:
type: integer
format: int64Параметры запроса
Параметры запроса отображаются в конце URL-адреса после знака вопроса (?). Несколько значений должны разделяться амперсандом (&).
GET /pets/findByStatus?status=available
GET /notes?offset=100&limit=50
Для определения таких параметров нужно использовать следующую конструкцию in: query.
paths:
/notes:
get:
parameters:
- name: offset
in: query
description: The number of items to skip before starting to collect the result se
schema:
type: integer
- name: limit
in: query
description: The numbers of items to return
schema:
type: integer
Примеры оформления параметров запроса.



Про параметры заголовка и куки подробнее можно прочитать в соответствующих разделах официальной документации OpenAPI.
Тело запроса
POST-, PUT- и PATCH-запросы могут иметь тело запроса.
Пользуйтесь ссылками на модели. Не нужно засорять нашу спецификацию перечислением того, что должно быть в моделях. Делаем ссылки для своего удобства и для удобства всей команды.
Есть исключения в виде массивов или групп, в таком случае мы прописываем массив и как тип элементов, которые там лежат, мы используем ссылку на модель элемента.
Пример:
requestBody:
required: true
content:
application/json:
schema:
$ref: "models.yaml#/components/schemas/RecalculateOrderRequest"Примеры оформления тела запроса.


Ответ
Описание REST-запроса обязательно должно содержать описание ответа (responses). Response задаётся HTTP-кодом ответа и данными, которые возвращаются в теле ответа и/или заголовке.
Описание ответа начинается с кода, такого как 200 или любого другого. Методы обычно возвращают один успешный код и один и более кодов ошибок. Каждый код требует описания (description) — условие, при котором код срабатывает. Если вы не можете задать определённый код, то его можно задать следующим видом: 1XX, 2XX, 3XX, 4XX, 5XX. Но таким образом в случае, если был задан код 404 и 4XX, приоритет у первого будет выше.
responses:
'201':
description: Бонусы успешно списаны.
'400':
description: Неверный запрос
content:
application/json:
schema:
$ref: "../common/models.yaml#/components/schemas/ResponseBadParameters"
'401':
description: Несанкционированный запрос
content:
application/json:
schema:
$ref: "../common/models.yaml#/components/schemas/ResponseUnauthorized"
'500':
description: |
Возможные ошибки
* `101` — UserBlocked, пользователь был заблокирован
* `104` — OTPCodeInvalid, неверный OTP-код
content:
application/json:
schema:
$ref: "../common/models.yaml#/components/schemas/ErrorResponse"
'426':
description: Необходимо обновить приложениеДля передачи файлов в запросе или ответе в OpenAPI 3.0 используется type: string и format: binary или format: base64.
paths:
/report:
get:
summary: Returns the report in the PDF format
responses:
'200':
description: A PDF file
content:
application/pdf:
schema:
type: string
format: binaryПримеры оформления ответа.


Статус-коды (основные)
Код | Описание | Часто используемые коды |
2xx | Операция завершилась успешно | 200 OK — ответ на успешные GET, PUT, PATCH, DELETE, а также для POST, который не привёл к созданию. 201 Created — используется в методах POST и имеет тело ответа, чтобы сказат�� клиенту, что мы создали в итоге — как минимум получить идентификатор записи. 202 Accepted — указывает на то, что запрос принят к обработке (обработка ещё не завершена или даже не начата), и клиенту необязательно ждать завершения операции. 204 No Content — операция прошла успешно, но тело ответа не требуется (например, запрос DELETE). |
3xx | Редирект или можем пойти читать из кэша | 304 Not Modified — свидетельствует о том, что данные не изменились и можно читать данные из кэша. Обычно работает с E-Tag или Cache-Control-заголовками. |
4xx | Операция завершилась с ошибкой по вине клиента | Из тех, что стоит фиксировать в спецификации: 400 Bad Request — сервер не смог понять запрос из-за недействительного синтаксиса. 401 Unauthorized — пользователь не авторизован для доступа. 403 Forbidden — пользователь не имеет права на запрашиваемый ресурс. 404 Not found — запрашивается несуществующий ресурс. 426 Upgrade Required — указывает на то, что сервер отказывается выполнять запрос с использованием текущего протокола, но может захотеть сделать это после того, как клиент обновится до другого протокола (используется, когда версия приложения уже не поддерживается и пользователю предлагается обновить приложение при получении данной ошибки). 429 Too Many Requests — запрос отклоняется из-за ограничения скорости (слишком много запросов за определённый промежуток времени). |
5xx | Операция завершилась с ошибкой по вине сервера (или не смог сразу определить, что по вине клиента) | Конкретные 5xx ошибки не фиксируем обычно в спецификации API, но если необходима необычная обработка, то фиксируйте (к примеру, нужна определённая заглушка на ошибку временной неработоспособности сервера — 503 Service Unavailable). |
Структура и проектирование моделей
В файл models.yaml я обычно выношу components, который в свою очередь включает:
schemas— модели,parameters— параметры.
Пример структуры файла:
components:
schemas:
UpdatedOrderResponse:
type: object
description: Модель для обновлённых полей заказа после выполнения действия над ним.
properties:
status:
$ref: "#/components/schemas/ExtendedOrderStatus"
actions:
type: array
description: |
Список действий, доступных над заказом.
Список пуст, если нет доступных действий.
items:
$ref: "#/components/schemas/OrderAction"
required:
- status
- actions
ReceiverType: # модель, которая содержит ограничения возможных значений по типу плательщика
type: string
enum: [individual, entity]
description: |
Тип плательщика:
- individual — физическое лицо
- entity — юридическое лицо
parameters: # параметры, которые можно переиспользовать в параметрах запроса
filter_type:
name: filter_type
in: query
description: |
Тип фильтра заказов пользователя:
- all — все заказы
- current — текущие
- done — выполненные
schema:
enum: ["all", "current", "done"]
type: string
Комментарий (description) и пример (example) — очень важная часть спецификации. Применимо как к методам, так и к моделям.
Уделяйте большое внимание этим полям и описывайте максимально понятно, вкладывайте контекст, логику, примеры — пишем как можно больше (в пределах разумного, конечно, описывать суперподробно user.name не стоит).
Типы параметров
С помощью ключевого слова type задаётся тип данных. Типы могут быть следующими (основные):
string— строка текста.number— включает в себя и целые числа, и числа с плавающей точкой.integer— только целые числа.boolean— в логическом типе boolean представлено два возможных значения: true и false.array— массив.object— объекты — коллекция пар «ключ и значение».
Строка
Длину строки можно ограничить, используя для этого
minLengthиmaxLength.Ключевое слово
patternпозволяет определить шаблон регулярного выражения для строки — значения, которые могут быть использованы в строке. Для заданияpatternиспользуется синтаксис регулярного выражения из JavaScript(pattern: '^\d{3}-\d{2}-\d{4}— конца строки. Без ^… $ шаблон соответствует любой строке, содержащей указанное регулярное выражение."
Ключевое слово
formatиспользуется для того, чтобы задать формат строки, например, один из них:date(2017-07-21),date-time(2017-07-21T17:32:28Z),password,byte,binary
К примеру, дл�� передачи файла используется:
avatar: # изображение, встроенное в JSON
description: Base64-encoded contents of the avatar
type: string
format: byte
Числа
Для указания диапазона возможных значений можно использовать ключевые слова
minimumиmaximum(minimum ≤value≤ maximum).Чтобы исключить граничные значения, укажите
exclusiveMinimum: trueиexclusiveMaximum: true.
count:
description: Суммарное количество товаров в заказе
type: integer
example: 6
maximum: 25
id:
description: Идентификатор пользователя
type: integer
format: int64Массивы
С помощью
minItemsиmaxItemsможно задавать минимальную и максимальную длины массива. Если не использоватьminItems, то массив может быть пустым.Элементы массива описываем отдельной моделью, если они представляют собой коллекцию.
# Элементы массива отдельной моделью
actions:
description: |
Список действий, доступных над заказом.
Список пуст, если нет доступных действий.
type: array
items:
$ref: "#/components/schemas/OrderAction"
# Массив строк
categories:
description: |
id категорий товаров первого уровня, в которые входят товары данной акции
type: array
items:
type: stringОбъекты
По умолчанию все элементы коллекции необязательные. Можно указать список обязательных элементов с помощью слова required.
Коллекция также может быть вложенной и включать в себя коллекцию. В таком случае коллекцию оформляем отдельным объектом и даём на него ссылку для удобства всех членов команды.
Проверка валидности спецификации OpenAPI
Минимальная проверка спецификации API может быть провед��на путём визуализации спецификации OpenAPI. Если вы делили спецификацию на разные файлы для методов и моделей, то визуализацию надо запускать, находясь в файле с методами.
Надо проверить, что спецификация визуализируется, все параметры отрендерены, прописаны обязательные и nullable-поля. Запросы и ответы также отрендерены, и не отображаются ошибки.
Если спецификация не рендерится, на что обратить внимание:
интервалы. Проверьте, что все объекты находятся на своём уровне;

ссылки. Проверьте, что все ссылки корректны и они ссылаются на существующие модели.
Также можно настроить линтер, который, например, будет запускаться каждый раз после того, как вы запушите свою спецификацию в ветку.
Дополнительная информация для ознакомления
Headers
Основные заголовки:
Accept-Charset — способ клиента сказать, в какой кодировке нужны данные (UTF-8, ASCII, whatever). Обычно всегда используется UTF-8, и менять не нужно.
Accept-Encoding (аналог с сервера — Content-Encoding) — то, как данные от сервера закодированы, обычно речь про алгоритм сжатия. Например, gzip.
Accept-Language (аналог с сервера — Content-Language) — то, какой язык хочет получить клиент. Использовать можно для мультиязычных сервисов.
Accept (аналог с сервера — Content-Type) — форматы данных, которые клиент поддерживает, эти форматы называются MIME-типами. Например, application/json. Такое часто бывает при передаче файлов или когда хотим открыть файл в вебе, здесь нужно правильно установить MIME-тип.
Cookie — это способ хранить состояние. Как это работает:
сначала сервер просит клиента установить cookies (Set-Cookie);
клиент при обращении отправляет их серверу в заголовке с ключом Cookie.
Если вы используете Cookie для передачи токена, то в таком случае обязательны параметры:
secure=truehttponly=truesamesite=strict
Общие правила при проектировании спецификации OpenAPI, которые были выработаны совместно с командой
Правило | Описание |
Использовать kebab-case для URL | Мы используем: В других командах могут делать и так: |
Использовать camelCase для параметров пути | Мы используем: В других командах могут делать и так: |
Использовать множественное число для коллекций | Мы используем: GET /users В других командах могут делать и так: |
Не использовать глаголы в URL ресурсов | Вместо этого используем HTTP-методы для описания операций. Мы используем: POST /courses/{courseId} или GET /courses В других командах могут делать и так: |
Использовать snake_case для JSON-свойств | Мы используем:
В других командах могут делать и так:
Не используем kebab-case:
|
Использовать глаголы в URL операций | Для функций, которые выполняют определённые действия на сервере и при этом не являются CRUD-операцией: Мы используем: |
Использовать простой порядковый номер для версий | Если поддерживаем версионирование API, то используем простой порядковый номер и всегда указываем его на самом верхнем уровне.
|
Указывать количество элементов в ответе на запрос | Если есть возможность возвращать общее количество элементов и это не скажется плохо на вашей производительности — возвращайте.
|
Не передавать аутентификационные токены в URL | Довольно плохая практика, потому что часто URL логируется и токен, соответственно, тоже сохранится. Есть, конечно, исключения, но в таком случае ИБ, по идее, должны проследить, чтобы это было максимально безопасно. Мы используем: заголовки авторизации и Cookies. Исключения: |
Использовать HTTP-методы для CRUD-операций | В этом и есть их смысл. Опять же — если вы следуете принципам REST. |
Заключение
В этой статье мы познакомились со структурой спецификации OpenAPI и примерами её использования.
Ещё раз напомню, что REST — это архитектурный стиль, а не стандарт. Поэтому всё сказанное в этой статье основано на реальном опыте системных аналитиков одной из ИТ-компаний нашей необъятной страны и не является призывом к обязательному применению.
В разных компаниях при использовании разных языков программирования или просто, потому что так «исторически сложилось», могут прибегать к тем или иным решениям, о которых мы в этой статье не поговорили или они не соответствуют описанному. Если статья для вас не имеет никакого смысла, буду рада услышать ваши комментарии о том, какие практики проектирования API используете вы.
В этой статье будет только одна РЕКОМЕНДАЦИЯ: придерживайтесь того, что принято в вашей компании и команде, а данная статья может стать неплохой основой для формирования корпоративного гайдлайна, если у вас такового нет 🙂
Всем добра!
