Skip to content

dtabolich/api-design-best-practices

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Лучшии практики ведения API спецификации (OpenAPI) в Surf

В этом репозитории находится пример спецификации (папка example) и описаны правила работы с ней, а также распространенные кейсы (файл cases).

Содержание


Общие принципы

  • Спецификация должна быть побита на разные файлы. Это необходимо для того, чтобы ее можно было легко и приятно поддерживать (дополнять, изменять) и вообще читать.
  • Корневой README.md должен содержать информацию о том, как работать с источниками в репозитории. Потому что кто-то может быть не в курсе.
  • Хорошо иметь скрипт для автогенерации шаблона. Чтоб не копипастить каждый раз самим.
  • Старайтесь использовать текстовый формат (не используйте GUI), потому что так проще навигировать. И так можно пользоваться поиском.
  • Относитесь к спецификации как к программному коду. Иначе в конечном итоге она превратится в мусор.

Организация файлов и папок

Хорошо придерживаться следующего паттерна:

- root_folder <- корень вашего репозитория
  -- README.md
  -- feature_or_api_controller <- папка, имеющая название фичи
    --- api.yaml        <- Файл исключительно для описание сервисов (path в Swagger)
    --- models.yaml     <- Файл исключительно для описания моделей (components/schemas в Swagger)
    --- parameters.yaml <- Файл исключительно для описания параметров api-методов (components/parameters в Swagger)
    --- errors.yaml     <- Файл исключительно для описания ошибок которые могут вернуть методы (components/schemas в Swagger)  

ВАЖНО

  • api.yaml и models.yaml обязательны! Потому что в противном случае люди будут бояться лезть менять ваши файлы.
  • parameters.yaml и errors.yaml не обязательны! Они могут быть использованы в тех случаях, когда сервисы содержат очень много повторяющихся параметров и/или ошибок.
  • Не стоит выносить отдельно components/requestBodies , потому что тогда, чтобы добраться от метода до модели, потребуется 2 перехода. Это уже неудобно.

Проектирование спецификации API

Перед проектированием спецификации API подразумевается, что вы:

  • Клонировали репозиторий и наполнили его файлами для генерации и readme.
  • Создали ветку фичи, которую будете проектировать.
  • Установили необходимые расширения в VSCode.

Инструменты

  • VSCode скачать и установить, если еще не установлен.
  • Расширения для VSCode - для установки переходим в раздел Расширения в левом боковом меню и ищем по названию необходимое расширение:
    • Swagger Viewer - Предварительный просмотр спецификации API, происходит в режиме реального времени по мере ввода.
    • GitHub Pull Requests and Issues (опционально) - можно смотреть и комментировать PR-ы прямо из VSCode. Необходимо авторизоваться через VSCode в GitHub, появится иконка GitHub-а в боковом меню, где можно просматривать и комментировать PR-ы.
    • Git Blame (опционально). Расширение Git Blame предоставляет возможность просматривать информацию состояния для текущей выбранной строки. Оно позволяет выяснить, кто писал определенные фрагменты кода.
    • Git History (опционально). Расширение Git History предоставляет возможность тщательно изучить историю файла, автора, ветки. Чтобы активировать окно Git History, нужно кликнуть на файл правой кнопкой мыши и выбрать Git: View File History. Кроме того, вы можете сравнивать ветки и коммиты, создавать ветки из коммитов и многое другое.

Генерация папок для фичи

Хорошо иметь какой-то инструмент, который позволит сгенерить нужные файлы с предзаполненной мета информацией - чтоб люди не писали по 100 раз одно и то же.

В этом репозитории это сделано с помощью шелл-скрипта gen.sh и двух файлов с шаблонами .api_template и .models_template

Сам скрипт позволяет сгенерировать "контроллер" с отдельным файлом для моделек и отдельным файлом для методов API. Т.к. мы проектируем уже фичу, то все махинации должны производиться в соответствующей ветке.

Пошаговая инструкция генерации папки для фичи:

Шаг
1 Проверяем, что мы в правильной ветке. Если работаем над авторизацией, то в ветке auth, которую мы создали.
2 Открываем консоль, перемещаемся в корень репозитория:
1. Идем в папку где располагается репозиторий (к примеру, E:\work\my-project-swagger).
2. Shift+клик по правой кнопка мыши → “Открыть окно PowerShell здесь”, на macOS соответственно терминал открываем или же через терминал VSCode.
3 Вводим в консоль ./gen.sh ./{название фичи (оно же название папки)}. Пример: ./gen.sh ./auth или sh gen.sh catalog
4 Скрипт создаст папку catalog и добавит в нее два файла api.yaml и models.yaml с уже заполненной шапкой. Если папка уже есть, то скрипт просто положит в нее два файла.
Всё, папки появились. Если нет, жмем кнопку обновления проводника (Explorer) в VSCode. Созданы два файла в папке auth с нужными шапками/шаблонами: api.yaml - для описания эндпоинтов, models.yaml - для описания моделей к ним
5 Можем начинать проектирование фичи, для которой создали папку.

Базовая структура

Структура файла api.yaml или что нагенерил мне скрипт:

Все поля, кроме path, заполняются при генерации папок. Но понимать, что они означают будет полезно.

Название поля Описание
openapi Обязательное для заполнения.
Номер версии спецификации OpenAPI. Последняя версия на данный момент 3.0.2.
Это поле не связано со строкой API - info.version.
info Обязательное для заполнения.
Содержит основную информацию о вашем API: название (title), описание вашего API (description), версия вашего API (version), контакты разработчика спецификации (contact.name, contact.email). В данной инструкции указан не весь набор элементов, которые могут содержаться в info-блоке, но для нашего использования этого достаточно.
Пример:
  info:
  title: "API"
  version: "1.0.0"
  contact:
    name: Юльская Виктория
    email: yulskaya@surfstudio.ru
servers Содержит информацию об используемых серверах: url и описание сервера (description). Их может быть несколько.
Пример:
  servers:
    - url: https://dev3.myproject.ru/api/surf/v1/
      description: Test server dev3
    - url: https://surf.myproject.ru/api/surf/v1/
      description: Test server surf
    - url: https://myproject.ru/api/surf/v1/
      description: Production Server
components В объекте components можно хранить множество различных переиспользуемых объектов. Объект components может содержать следующее:
  • схемы
  • ответы
  • параметры
  • примеры
  • тело запроса
  • хэдеры
  • схемы безопасности
  • и тд
    При делении папки на два файла api.yaml и models.yaml нам данный блок нужен только для определения схемы безопасности, все остальное уходит в файл models.yaml.
    Если спецификация заказчика вся ведется в одном файле, то все вышеперечисленное будет в данном блоке.

    Лучше сразу понять как будет проходить авторизация и дальнейшая проверка наличия прав пользователя при запросе на ресурс - через токены / куки / что-то другое (разработчикам надо закладывать это в самом начале, во время первого спринта, а то и во время инициализации).

    Пример:
      components:
        securitySchemes:
          bearerAuth:
            type: http
            scheme: bearer
            bearerFormat: JWT 
  • security Для отправки запросов, авторизованных нашим сервером API, спецификация должна содержать информацию о безопасности, которая авторизует запрос.
      security:
        - bearerAuth: []
    Объявленные поля components.securitySchemes и security свидетельствуют о том, что у любого метода в этом файле должен быть установлен хедер Authorization с JWT токеном.
    При этом у каждого метода можно определить секцию security:[] , оставив ее пустой, которая будет свидетельствовать о том, что для данного метода авторизация не нужна.
    paths Обязательное для заполнения.
    Содержит доступные пути (конечные точки) и операции (методы) для API.
    Подробнее о заполнении данного блока рассмотрим чуть ниже.

    Ресурсы и методы

    Блок path состоит из:

    • пути (конечной точки) - все пути в блоке 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).

    Стараемся делать как можно больше документов и коллекций, и как можно меньше контроллеров

    В названии пути НЕ ПИШЕМ ДЕЙСТВИЕ, о котором говорит HTTP method!!! (create, update, delete и тд)

    • POST /courses - Создать новый курс
    • POST /courses/create - Создать новый курс

    Методы

    Метод Описание Комментарий
    GET Возвращает представление ресурса по указанному универсальному коду ресурса (URI). Текст ответного сообщения содержит сведения о запрашиваемом ресурсе.
  • НЕ СОДЕРЖИТ ТЕЛА
  • Прокси могут просто отбрасывать тело GET запроса.
  • OS может самостоятельно повторить GET запрос.
  • GET запросы по умолчанию кешируются через URI. Если параметры передаются в теле, то кеши работать не будут.
  • POST Создает новый ресурс по указанному URI. Текст запроса содержит сведения о новом ресурсе.
    Метод POST также можно использовать для запуска операций, не относящихся непосредственно к созданию ресурсов (для операции контроллера).
    PUT Создает или заменяет ресурсы по указанному URI.
    В тексте сообщения запроса указан создаваемый или обновляемый ресурс.
    Лучше все таки разделять создание (делать POST) и изменение (PUT / PATCH)
  • Полностью перезаписывает ресурс
    image
  • PATCH Выполняет частичное обновление ресурса. Текст запроса определяет набор изменений, применяемых к ресурсу
  • Перезаписывает только определенную часть
    image
  • 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/{id}:
    GET /cars/{carId}/drivers/{driverId}:

    Каждый параметр пути должен быть заменен фактическим значением при вызове.

    Для определения параметров пути нужно использовать следующую конструкцию in: path. Необходимо также добавить required: true, чтобы указать обязательность данного параметра.

    paths:
      /users/{id}:
        get:
          parameters:
            - name: id   # имя можно использовать такое же как и в пути
              in: path
              description: Идентификатор пользователя
              required: true # обязательный параметр
              schema:
                type: integer
                minimum: 1

    Параметры запроса

    Параметры запроса отображаются в конце 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 set
              schema:
                type: integer
            - name: limit
              in: query
              description: The numbers of items to return
              schema:
                type: integer

    Не декларируйте здесь объекты!!!

    Примеры оформления параметров запроса
    Не надо так Надо вот так Так тоже можно
    nope norm1 Если все же необходим объект, то объявите его в моделях и ссылайтесь на данный объект
    norm2
    Можно вынести отдельно параметры для дальнейшего переиспользования и ссылаться на объявленные параметры
    norm3

    Про параметры заголовка и куки подробнее можно прочитать в соответствующих разделах.

    Тело запроса

    POST, PUT и PATCH запросы могут иметь тело запроса.

    1. Мы всегда должны ставить ссылки на модели!!! Не нужно засорять нашу спецификацию перечислением того, что должно быть в моделях. У нас и так огромные методы, а если еще модели писать, то будет много дублирования и иных проблем. Делаем ссылки для своего удобства и для удобства всей команды.
    2. Есть исключения в виде массивов или групп, в таком случае мы прописываем массив и как тип элементов, которые там лежат мы используем ссылку на модель элемента.

    Пример:

    requestBody: 
      required: true
      content:
        application/json: 
          schema:
            $ref: "models.yaml#/components/schemas/RecalculateOrderRequest"
    Примеры оформления тела запроса
    Плохо Хорошо
    image image

    Ответ

    Описание REST-запроса обязательно должно содержать описание ответа (responses). У каждого метода должен быть определен хотя бы один ответ (успешный). Response задается HTTP-кодом ответа и данными, которые возвращаются в теле ответа и / или заголовке.

    1. Мы всегда должны ставить ссылки на модели!!! Не нужно засорять нашу спецификацию перечислением того, что должно быть в моделях. У нас и так огромные методы, а если еще модели писать, то будет много дублирования и иных проблем. Делаем ссылки для своего удобства и для удобства всей команды.
    2. Есть исключения в виде массивов или групп, в таком случае мы прописываем массив и как тип элементов, которые там лежат мы используем ссылку на модель элемента.

    Описание ответа начинается с кода, такого как 200 или 404. Методы обычно возвращают один успешный код и один и более кодов ошибок. Каждый код требуется описания (description) - условие, при которых код срабатывает. Если вы не можете задать определенный код, то его можно задать следующим видом: 1XX, 2XX, 3XX, 4XX, 5XX. Но таким образом, в случае если был задан код 404 и 4XX, приоритет у первого будет выше.

    responses:
            '201':
              description: Бонусы успешно списаны.
            '500':
              description: |
                Возможные ошибки
                * `101` - UserBlocked, пользователь был заблокирован
                * `104` - OTPCodeInvalid, неверный OTP-код
              content:
                application/json:
                  schema:
                    $ref: "../common/models.yaml#/components/schemas/ErrorResponse"
            '426':
              description: Необходимо обновить приложение

    Не стоит описывать все возможные коды ответов, тем более что о некоторых из них можно и не знать в момент проектирования запроса. Важно при описании кодов покрыть случай успешного выполнения запроса и коды ошибок, известных на момент написания REST-запроса.
    Также, в Surf обычно описываются кастомные ошибки и заворачиваются в 400 или 500 статус код.
    Кастомные ошибки описываются в отдельной папке common - где в api.yaml описывается справочник ошибок, а в models.yaml описывается модель ошибки, к примеру, ErrorResponse, который состоит из кода кастомной ошибки, сообщения в человекочитаемом виде, и при необходимости вспомогательная информация об ошибке

    Для передачи файлов в запросе или ответе в 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
    Примеры оформления ответа
    Плохо Хорошо
    image
    image
    image

    Статус коды

    Код Описание Часто используемые коды
    2xx Операция завершилась успешно
  • 200 - Все ок!
    Всегда содержит тело ответа. Может использоваться в GET запросах.
  • 201 - Запись создана.
    Используется в методах POST и имеет тело ответа, чтобы сказать клиенту, что мы создали в итоге - как минимум получить идентификатор записи).
  • 202 - Принято.
    Не содержит тело ответа.
    Говорит о том, что клиенту не обязательно ждать завершения операции (но она еще не завершилась). Пример, оплата.
  • 204 - Операция прошла успешно, но ответ пустой.
    Если знаем что мы не ждем ответа, то ставим всегда данный код.
  • 3xx Редирект или можем пойти читать из кэша
  • 304 - Данные не изменились.
    Можно читать данные из кеша. Обычно работает с E-Tag или Cache-Control заголовками.
    Работает только с GET запросами
  • 4xx Операция завершилась с ошибкой по вине клиента Из тех, что стоит фиксировать в спецификации:
  • 401 - Пользователь не авторизован для доступа.
  • 403 - Пользователь не имеет права просматривать контент.
  • 426 - Указывает, что сервер отказывается выполнять запрос с использованием текущего протокола, но может захотеть сделать это после того, как клиент обновится до другого протокола (используется когда версия приложения уже не поддерживается и пользователю предлагается обновить приложение при получении данной ошибки).
    Также, на 400 или 409 можно повесить кастомные ошибки и описать их в справочнике
  • 5xx Операция завершилась с ошибкой по вине сервера (или не смог сразу определить что по вине клиента) Конкретные 5xx ошибки не фиксируем обычно в спецификации API, но если необходима необычная обработка, то фиксируйте (к примеру определенная заглушка на ошибку временной неработоспособности сервера - 503)

    Структура и проектирование моделей

    Файл 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) очень важная часть спецификации. Применимо как к методам, так и к моделям.
    Уделяйте большое внимание этому полю и описывайте как можно более понятнее, вкладывайте контекст, логику, примеры - пишем как можно больше (в пределах разумного, конечно, описывать супер подробно 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

    Массивы

    • С помощью minItems и maxItems можно задавать минимальную и максимальную длину массива. Если не использовать minItems, то массив может быть пустым.
    • Элементы массива описываем отдельным элементом, если они представляют собой коллекцию.
    # Элементы массива отдельным элементом
    actions:          
      description: |
        Список действий, доступных над заказом. 
        Список пуст, если нет доступных действий. 
      type: array
      items:
        $ref: "#/components/schemas/OrderAction"
    
    # Массив строк
    categories:
      description: |
        id категорий товаров первого уровня, в которые входят товары данной акции 
      type: array
      items:
        type: string

    Объекты

    • По умолчанию все элементы коллекции необязательные. Можно указать список обязательных элементов с помощью слова required (можно отказаться от required и прийти к тому, чтобы явно прописывать параметрам nullable: false, если поле не может быть пустым).
    • Коллекция также может быть вложенной и включать в себя коллекцию. В таком случае коллекцию оформляем отдельным объектом и даем на него ссылку для удобства всех членов команды. image

    Ошибки, хэширование, кэширование и тд

    Для описания данной информации следует создать папку common, где фиксировать общие договоренности, которые в самих методах никак не отразить. Также, можно выносить общие методы, к примеру, загрузка изображения (профиля, отзыва и тд).

    Пример оформления файла common/api.yaml
    openapi: 3.0.2
    info:
      title: "API"
      version: "1.0.0"
      contact:
        name: Виктория Юльская
        email: yulskaya@surfstudio.ru
      description: |
        # Headers
        <details>
        Для определения текущего пользователя, во всех запросах может как приходить, так и передаваться кастомный хедер `UserID`.
        </details>
    
        # Авторизация 
        <details>
    
        Схема авторизации онована на механизме access/refresh токенов.
    
        При логине - сервер возвращает пару access/refresh токен. 
        В дальнейшей работе с сервером во всех запросах необходимо передавать в хедере `Authorization`
        полученный access токен.
        При его протухании - необходимо произвести обновление токенов с помощью соответствующего метода,
        используя refresh токен.
    
        При получении ошибки во время обновления токенов - необходимо закончить текущую сесию работы с сервером
        и разлогинить пользователя.
        </details>
    
        # Пагинация
        <details>
    
        Проект поддерживает страничную пагинацию.
        Каждый пагинируемый запрос должен url-параметрами принимать `page` и `size`,
        а в теле ответа возвращать данные с информацией о порции пагинируемых данных. 
        
        - `page` отвечает за размер пачки пагинации, 
        - `size` отвечает за размер порции.
        </details>
    
        # Ошибки 
        <details>
        <summary>Описание</summary>
    
        Сервер возвращает ответ со статус кодом 400 и ошибкой в формате:
    
            { 
              "code": 100,
              "errorMessage": "Пользователь не найден",
              "data": "85"
            }
    
        Ошибки состоят из
        - специфического кода `code` для идентификации ошибки
        - текста `errorMessage`, который будет отображаться на клиенте
        (в силу этого вся обязанность за формирование текста ошибок ложиться на серверную часть,
        что позволит обеспечить гибкость механики в целом)
        - и опционального поля `data`, в котором может располагаться строка с какими-то данными, 
        которые клиенту следует отобразить 
        (Например, это может быть количество секунд, через которые можно повторно отправить смс)
    
        </details>
        
        <details>
        <summary>Специфические коды ошибок</summary>
    
        * `100` - BadJson, неверный формат JSON от клиента
        * `103` - OTPCodeRequestTooOften, слишком частые запросы на генерацию OTP-кода, в **data** - количество секунд до разблокировки
        * `104` - OTPCodeInvalid, неверный OTP-код
        * `105` - SendingCodeFailed, не удалось послать OTP-код
        * `106` - FileUploadingError - Ошибка при загрузке файла
        * `107` - NotEnoughPoints - Недостаточно баллов для списания
        * `108` - NotEnoughProducts - Товар закончился
        * `120` - NameNotUnique - Товар с таким именем уже существует
        * `119` - RefreshTokenFailed - Не удалось обновить токен
        * `121` - OTPCodeExpired - срок жизни кода истек
        * `122` - BonusesWriteoffFailed - Нельзя списать больше бонусов, чем есть у пользователя
    servers:
      - url: https://someaddress.com
        description: TODO заглушка, требуется поправить в будущем на реальный адрес. 
    
    components:
      securitySchemes:
        bearerAuth:
          type: http
          scheme: bearer
          bearerFormat: JWT 
    
    security:
      - bearerAuth: []
    
    paths:
      /file:
        post:
          summary: Загрузка файла
          description: Запрос на загрузку файла на сервер, используется, когда пользователь прикрепляет файлы
          requestBody:
            required: true
            content: 
              multipart/form-data:
                schema:
                  $ref: "models.yaml#/components/schemas/BinaryFile"
          responses:
            '200':
              description: Успешный ответ с данными.
              content:
                application/json:
                  schema:
                    $ref: "models.yaml#/components/schemas/FileURL"
            '400':
              description: |
                Возможные ошибки
                 * `106` - FileUploadingError - Ошибка при загрузке файла
              content:
                application/json:
                  schema:
                    $ref: "../common/models.yaml#/components/schemas/ErrorResponse"
        
      /file/{fileUrl}:
        delete:
          summary: Удаление файла
          parameters:
            - name: fileUrl
              in: path
              description: Ссылка на файл
              required: true
              schema:
                type: string
          responses:
            '204':
              description: Файл удален

    Быстрые и полезные комбинации

    1. Ctrl (или CMD) - Можем посмотреть что за ref у нас в методе / моделе. Наводим на ref и зажимаем комбинацию, получаем информацию о нашей ссылке. Если кликнуть по ней, то быстро перейдем по ссылке к нашей модели, на которую ссылаемся.
    2. Ctrl (или CMD) + Shift + O - Позволяет найти и перейти к конкретной вкладке, либо конкретной модели / свойству.
    3. Ctrl (или CMD) + Shift + P - Открывает панель команд.
    4. Ctrl (или CMD) + P - Поиск файлов по имени

    Проверка спецификации API на фичу

    Минимальная проверка спецификации API на фичу может быть проведена путем визуализации спецификации API при помощи комбинации Alt+Shift+P. Комбинацию вызывать находясь в файле api.yaml.

    Проверить что спецификация визуализируется, все параметры отверстаны, прописаны обязательные и nullable поля. Запросы и ответы также отрендерены и не отображаются ошибки.

    Если не рендерится сваггер, на что обратить внимание:

    Табуляция. Проверьте, что все находится на своем уровне

    Ссылки. Проверьте, что все ссылки корректные и они ссылаются на существующие модели.

    Проблема с табуляцией Все ок
    image image

    Проверка всей спецификации API через линтер SurfGen

    Для проверки должен быть подключен SurfGen и подключен линтинг через GitHub Actions.

    Для корректной работы ветки называть feature_branchname, к примеру, чтобы у всех веток (кроме мастера) был единый префикс.

    Далее при открытии PR-а, при пуше изменений и тд будет запускать проверка спеки на соответствие всего вышеописанного.

    • Если проставилась зеленая галка, то все с вашей спекой ок.
    • Если видим красный индикатор, то есть проблемы и их нужно исправить.

    Идем на вкладку Checks и читаем логи (build). Начинаем читать все, начиная с третьей строки. Что мы видим?

    • Линтер пошел парсить схему /common/models.yaml
    • Зашел в объект ErrorResponse
    • В параметре data нашел ошибку- Выдает нам описание ошибки.

    Чтобы исправить ошибку мы идем в папку common -> файл models.yaml -> находим модель ErrorResponse, а в ней параметр data. Исправляем описанную линтером ошибку.

    image

    После того, как мы запушим изменения с исправлением линтер снова запустится и продолжит проверку. Исправляем до тех пор, пока не получим зеленую галку)

    Дополнительная информация для ознакомления и советы

    Хэдеры

    Основные дефолтные хэдеры:

    • Accept-Charset - способ клиента сказать в какой кодировке нужны данные (UTF-8, ASCII, whatever). Обычно всегда используется UTF-8 и менять не нужно.
    • Accept-Encoding (аналог с сервера - Content-Encoding) - то, как данные от сервера закодированы, обычно речь про алгоритм сжатия. Например, gzip.
    • Accept-Language (аналог с сервера - Content-Language) - то, какой язык хочет получить клиент. Использовать можно для мультиязычных сервисах.
    • Accept (аналог с сервера - Content-Type) - Формат данных которые клиент поддерживает, эти форматы называются MIME-типами. Например, application/json. Такое часто бывает при передаче файлов или когда хотим открыть файл в вебе, здесь нужно правильно установить MIME-тип.
    • Cookies - это способ хранить состояние. Как это работает:
      • Сначала сервер просит клиента установить cookies (Set-Cookie).
      • Клиент их отправляет серверу при обращении в хэдерах с ключом Cookie.

    Cookies могут использоваться для передачи токена. Не самый лучший способ, но такое может быть.
    В таком случае обязательные параметры для таких cookies:

  • secure=true
  • httponly=true
  • samesite=strict

  • Советы

    Совет Описание
    Используйте kebab-case для URL Вот пример для списка заказов.
    Плохо: /systemOrders или /system_orders
    Хорошо: /system-orders
    Используйте camelCase для параметров Вот пример получения списка продуктов в магазине.
    Плохо: /system-orders/{order_id} или /system-orders/{OrderId}
    Хорошо: /system-orders/{orderId}
    Используйте множественное число для коллекций Если вы хотите получить всех пользователей.
    Плохо: GET /user или GET /User
    Хорошо: GET /users
    Не используйте глаголы в URL ресурсов Вместо этого пользуйтесь HTTP методами для описания операций.
    Плохо: POST /updateuser/{userId} или GET /getusers
    Хорошо: PUT /user/{userId}
    Пользуйтесь глаголами в URL операций Например, если вы хотите переслать уведомление пользователю.
    Хорошо: POST /alerts/245743/resend
    Помните, что resend не является [CRUD](https://ru.wikipedia.org/wiki/CRUD) операцией. Наоборот, это функция, которая выполняет определённое действие на сервере.
    Используйте camelCase для JSON свойств Вместо этого пользуйтесь HTTP методами для описания операций.
    Плохо:
    {
       user_name: "Ванька Петров",
       user_id: "1"
    }

    Хорошо:

    {
       userName: "Ванька Петров",
       userId: "1"
    }
    Используйте простой порядковый номер для версий И всегда указывайте его на самом верхнем уровне.
    Хорошо: http://api.domain.com/v1/shops/3/products
    Указывайте количество ресурсов в ответе на запрос Это свойство можно назвать total.
    Плохо:
    {
      users: [ 
         ...
      ], offset: 0
    }

    Хорошо:

    {
      users: [ 
         ...
      ] offset: 0,
        total: 34
    }
    Используйте параметры limit и offset И всегда указывайте его на самом верхнем уровне.
    Хорошо: GET /shops?offset=5&limit=5
    Потому что на фронтенде часто требуется пагинация
    Не передавайте аутентификационные токены в URL И всегда указывайте его на самом верхнем уровне.
    Это очень плохая практика, потому что часто URL логгируются, и токен также сохранится.
    Плохо: GET /shops/123?token=some_kind_of_authenticaiton_token
    Хорошо: Вместо этого пользуйтесь заголовками. Authorization: Bearer xxxxxx, Extra yyyyy
    Помните — время жизни токена нужно ограничивать
    Используйте HTTP методы для CRUD операций В этом и есть их смысл.
  • GET: получение данных о ресурсах.
  • POST: создание новых ресурсов и подресурсов.
  • PUT: обновление существующих ресурсов.
  • PATCH: обновляет только определённые поля существующих ресурсов.
  • DELETE: удаляет ресурсы.
  • URL должен отражать структуру вложенных ресурсов Примеры:
  • GET /shops/2/products: получить список продуктов из магазина 2.
  • GET /shops/2/products/31: получить детали продукта 31 из магазина 2.
  • DELETE /shops/2/products/31: удалить продукт 31 из магазина 2.
  • PUT /shops/2/products/31: обновить данные о продукте 31. Используйте PUT на URL ресурса, а не коллекции.
  • POST /shops: создать новый магазин и вернуть данные о нём. Используйте POST на URL коллекции.
  • About

    No description, website, or topics provided.

    Resources

    License

    Stars

    Watchers

    Forks

    Releases

    No releases published

    Packages

    No packages published

    Languages

    • Shell 100.0%