Пишем документацию API при помощи RAML

  • Tutorial
RAML

Удобство работы с любым API во многом зависит от того, как написана и оформлена его документация. Cейчас мы ведём работу по стандартизации и унификации описания всех наших API, и вопросы документирования для нас особенно актуальны.
После долгих поисков мы решили оформлять документацию в формате RAML. Так называется специализированный язык для описания REST API. О его возможностях и преимуществах мы расскажем в этой статье.

Почему RAML


Как нужно документировать API? Вопрос этот не так прост, как может показаться на первый взгляд.
Первый и самый простой вариант, который приходит на ум — представить описание к API в виде обычного текстового документа. Так делают очень многие (в том числе и очень известные компании). Мы тоже не раз пользовались этим способом. При всей своей простоте он обладает следующими недостатками:

  • текстовую документацию сложно поддерживать в актуальном состоянии;
  • зачастую словесные описания API оказываются недостаточно наглядными;
  • сфера использования «словесной» документации очень ограничена (например, на её основе нельзя сгенерировать интерактивную тестовую страницу).

Чтобы упростить процесс документирования, можно использовать специализированные инструменты и сервисы. Как правило, они генерируют документацию на основе описания в некотором стандартизованном формате — обычно это JSON или Markdown.

Ни один из этих форматов для написания документации не подходит. JSОN был изначально создан для обмена данными в вебе. При использовании его для других целей поневоле приходится прибегать к помощи «костылей» — например, кастомных полей, начинающихся со знака $. Кроме того, составлять описания в формате JSON вручную — дело достаточно рутинное и утомительное (в особенности если речь идёт об описаниях большого размера).

На описанные выше трудности обращали внимание многие пользователи популярного инструмента Swagger. Вскоре разработчики Swagger решили упростить работу по написанию спецификаций и создали фирменный редактор с поддержкой формата YAML.

Конечно, YAML гораздо удобнее, чем JSON. Но и его использование сопряжено с определёнными трудностями. Дело в том, что в описаниях API всегда имеются повторяющиеся элементы (например, схема ответа, которая может быть одинаковой для разных типов HTTP-запросов), которые приходится всякий раз прописывать вручную. Вот если бы можно их было раз и навсегда прописать в отдельном файле и ссылаться на него в случае небходимости… Но, увы, пока что такой возможности нет.

Что касается формата Markdown (он используется, например, в API BluePrint), то предназначен в первую очередь для оформления текста, а не для использования в качестве основы для генерирования. Приспособить его под документирование API очень сложно. По этой же причине не привели к каким-либо заметным результатам попытки cоздать формат описания API на базе XML — например, язык WADL (Web Application Desription Language), разработанный компанией Sun Microsystems ещё в 2009 году, но так и не получивший широкого распространения.

Создатели проекта RAML (эта аббревиатура означает RESTful API Modeling Language — язык для моделирования REST API ) предприняли попытку разработать язык, предназначенный исключительно для описания API и исправить недочёты, свойственные другим форматам. Первая версия спецификации RAML была опубликована в 2013 году. Основным разработчиком RAML является компания MuleSoft; в проекте также принимают участие представители таких известных компаний, как Cisco, PayPal, ВoxInc. и других.

Несомненными преимуществами RAML являются:
  • простой и логичный синтаксис, основанный на формате YAML;
  • поддержка наследования и возможность подключения внешних файлов спецификаций.

Дополнительным плюсом является наличие большого количества конвертеров, парсеров и генераторов интерактивной документации. О некоторых из них мы расскажем ниже, а пока перейдём к обзору особенностей синтаксиса RAML.

Краткое введение в RAML


Структура документа


Файл спецификаций на RAML состоит из следующих структурных элементов:

  • вводная часть («шапка»);
  • схема безопасности;
  • описание профилей;
  • описание объектов и методов;
  • описание ответов.

Рассмотрим эти элементы подробнее.

Вводная часть


Каждый документ на RAML начинается с вводной части, которая включает четыре обязательных элемента:

  • версия RAML;
  • имя документа;
  • URI, по которому доступен API;
  • версия API, описываемая в документации.

Выглядит это так:

#% RAML 0.8
title: Example API
baseUri: http://api.example.com/{version}
version: v1

Вводная часть может также включать различную дополнительную информацию — например, сведения об используемом протоколе для связи с API:

protocols: [http, https]

Можно во вводной части прописать и метаданные файла документации:

documentation
    - title: Home
        content: |
                  API Test Documentation


Схемы безопасности


Чтобы начать работать с любым API, нужно пройти процедуру авторизации. Она может осуществляться разными способами: через OAuth, с помощью токенов, либо посредством простой HTTP-аутентификации. Для описания этой процедуры в RAML используются схемы безопасности (security schemes).
Рассмотрим в качестве примера, как описывается авторизация с использованием протокола OAuth2:
#%RAML 0.8
title: Example API
version: 1
baseUri: https://api.example.com/{version}
securedBy: [oauth_2_0]
securitySchemes:
    - oauth_2_0:
               type: OAuth 2.0
        describedBy:
            headers:
                Authorization:
                         type: string
            queryParameters:
                access_token:
                      type: string
            responses:
                401:
                    description: |
                        Bad or expired token.                 
                 403:
                    description: |
                        Bad OAuth request 
        settings:
          authorizationUri:  https://example.com/oauth/authorize         
          accessTokenUri: https://example.com/oauth/token
          authorizationGrants: [ code, token ]

Приведённый фрагмент содержит следующую информацию:

  • в параметре type указывается, что в API используется авторизация по протоколу OAuth2;
  • далее указывается, что авторизационные данные можно передавать либо в заголовке Authorization, либо в query-параметре access_token;
  • после этого следуют возможные коды ответов и их описания;
  • в конце раздела, в секции settings указываются URL для авторизации, URL для получения токена, а также необходимые для аутентификации параметры (authorization grants).

Для удобства схемы безопасности можно сохранять в отдельных файлах .raml или .yml, и затем обращаться к ним в случае необходимости:

#%RAML 0.8
title: Example API
version: 1
baseUri: https://api.example.com/{version}
securedBy: [oauth_2_0]
securitySchemes:
    - oauth_2_0: !include oauth_2_0.yml

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

Почитать более подробно о схемах безопасности и ознакомиться с конкретными примерами можно здесь(раздел Security).

Объекты и методы


Далее перечисляются основные объекты и пути к ним, а также HTTP-методы, которые используются с этими объектами:

/document
 get:
 put:
 post:
  /{documentId}
   get:
   delete:

В приведённом примере описывается API, с помощью которого можно работать с документами. Мы можем скачивать документы на локальную машину (GET), изменять cуществующие документы (PUT) и загружать новые (POST). С каждым отдельным документом ({documentId}) мы можем также выполнять следующие операции: загрузка на локальную машину (GET) и удаление (DELETE).
HTTP-заголовки, используемые с тем или иным методом, описываются при помощи свойства headers, например:

/documents
   get
     headers:
        X-Auth-Token:
        required: true

Обратите внимание на свойство required: оно указывает, является ли заголовок обязательным (true) или необязательным (false).
В описании объектов и методов могут использоваться многочисленные дополнительные параметры. Рассмотрим следующий пример:

/document
      /{documentId}
              uriParameters:
                       id:
                        description: document identification number
                        type: string
                        pattern: ^[a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$

Здесь мы указываем, что каждый из документов, доступ к которым можно получить через API, имеет собственный идентификационный код в виде строки (type: string), а также описываем формат этого кода с помощью регулярных выражений.

Свойства description, type и pattern можно использовать и в описаниях методов, например:
/documents
   get:
        description: Retrieve a list of documents
   post:
         description: Add a new document
   body: 
          application/x-www-form-urlencoded
                 formParameters:  
                         id: 
                           description: document identification number
                           type: string
                           pattern: [a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$
                         name:
                             description: the name of the document
                             type: string
                             required: true
                          author: 
                                description: The name of the author
                                type: string
                                 required: true

В описании метода POST мы указываем параметры, которые нужно передать в теле запроса для добавления нового документа: ID, имя и имя автора. Каждый из этих параметров является строкой (type: string). Обратите внимание на свойство required: оно указывает, является ли параметр обязательным (true) или необязательным (false).

Для каждого метода можно прописать индивидуальную схему безопасности:

/documents
  get
      [securedBy: oauth_2_0]


Query-параметры


Для каждого метода в документации можно указать query-параметры, которые будут использоваться в запросе. Для каждого query-параметра указываются следующие характеристики: имя, тип, описание и пример:

 /document:
     get:
       queryParameters:
         author:
           displayName: Author
           type: string
           description: The author's full name
           example: Ivan Petrov
           required: false
         name:
           displayName: Document Name
           type: string
           description: The name of the document
           example: Delivery contract
           required: false
         signingDate:
           displayName: signingDate
           type: date 
           description: The date when the document was signed
           example: 2015-07-15
           required: false


Профили


Чтобы избежать лишних повторений в описаниях, в RAML используются профили (traits), которые прописываются во вводной части документа:

#% RAML 0.8
title: Example API
baseUri: http://api.example.com/{version}
version: v1

traits:
 - searchable:
    queryParameters:
         author:
           displayName: Author
           type: string
           description: The author's full name
           example: Ivan Petrov
           required: false
         name:
           displayName: Document Name
           type: string
           description: The name of the document
           example: Delivery contract
           required: false
         signingDate:
           displayName: signingDate
           type: date 
           description: The date when the document was signed
           example: 2015-07-15
           required: false

В дальнейшем к профилю можно обращаться при описании любых методов:

/document:
     get:
            is: [searchable]

Более подробно о профилях и особенностях их использования можно почитать в официальной документации (раздел Traits).

Описание ответа


В описании ответа обязательно указывается его код. Также в описание можно добавить схему (schema) — перечисление входящих в ответ параметров и их типов. Можно также привести пример конкретного ответа (example).

 /documents:
   /{documentId}:
     get:
       description: Retrieve document information
       responses:
         200:
          body:
           application/json:
             schema |
               {"$schema": “http://json-schema.org/schema”,
                "type":"object"
                "description": "a document"
                "properties": {
                     "id":{"type":"string"}
                     "name":{"type":"string"}
                     "author":{"type":"string"}
                     "signingDate": {"type":"date"}
               example: |
               {"data:" {
                  "id": "DOC3456"
                  "name": "New Delivery Contract"
                  "author": "Ivan Petrov"
                  "signingDate": "2015-05-20"
                },
                "success": true
                "status": 200
              } 

Cхемы ответов можно сохранять в отдельных файлах формата .yml или .raml и обращаться к ним в других частях документации:

schemas:
 - !include document-schema.yaml

/articles:
 get:
  responses:
   200:
    body:
     application/json:
      schema: document

Визуализация и генерация документации


RAML2HTML и другие конвертеры


Несмотря на то, что RAML — формат относительно новый, для него уже разработано достаточное количество конвертеров и парсеров. В качестве примера можно привести ram2htmtl, генерирующий на основе описания в формате RAML статическую HTML-страницу.
Устанавливается он при помощи менеджера пакетов npm:

$ npm -i g raml2html

Чтобы сконвертировать RAML-файл в HTML, достаточно выполнить простую команду:
$ raml2html api.raml > index.html


Поддерживается возможность создания собственных шаблонов для HTML-файлов (подробнее об этом см. в документации на GitHub по ссылке выше).
Из других конвертеров рекомендуем также обратить внимание на RAML2Wiki и RAML2Swagger.

API Designer


Компания Mulesoft (один из активных участников проекта RAML) создала специальный онлайн-инструмент, с помощью которого можно упростить работу по написанию документации и последующему тестированию API. Называется он API Designer.
Чтобы начать им пользоваться, нужно сначала зарегистрироваться на сайте. После этого можно приступать к работе. API designer предоставляет, во-первых, удобный интерактивный редактор для написания документации онлайн, а во-вторых — платформу для тестирования.
Выглядит всё это так:

API Designer

В правой части страницы автоматически генерируется интерактивная документация. Здесь же можно и протестировать API: для этого достаточно просто развернуть описание нужного запроса и нажать на кнопку Try it.

API Designer позволяет также загружать RAML-файлы с локальной машины. Поддерживается импорт файлов описаний API для Swagger.
Кроме того, API Designer хранит статистику тестовыx запросов к API.

API console


API console — полезный инструмент, разработанный всё той же компанией MuleSoft. С его помощью можно прямо в браузере генерировать документацию к API. Файлы спецификаций можно как загрузить с локальной машины, так и указать ссылку на внешний источник:

API Console

В состав API Console входит несколько файлов-образцов, представляющих собой описания API популярных веб-сервисов: Twitter, Instagram, Box.Com, LinkedIn. Мы попробовали сгенерировать на основе одного из низ документацию — выглядит вполне симпатично:

API Console

Документация, получаемая на выходе, является интерактивной: в ней можно не только прочитать описание API, но и выполнить тестовые запросы.

Заключение


В этой статье мы рассмотрели основные возможности RAML. Его несомненными преимуществами являются простота и логичность. Надеемся, что в скором будущем RAML получить ещё более широкое распространение и займёт достойное место в ряду инструментов для документирования API.
Если у вас есть вопросы — добро пожаловать в комментарии. Будем также рады, если вы поделитесь собственным опытом использования RAML на практике.

Если вы по тем или иным причинам не может оставлять комментарии здесь — добро пожаловать в наш блог.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Как вы описываете документацию API?

Selectel

192,00

ИТ-инфраструктура для бизнеса

Поделиться публикацией
Комментарии 39
    0
    >Вот если бы можно их было раз и навсегда прописать в отдельном файле и ссылаться на него в случае небходимости… Но, увы, пока что такой возможности нет.

    Как нет? А схемы и $ref? (сотая строка в примере editor.swagger.io )

    И в догонку: документация это хорошо, а валидация данных по RAML-схемам есть?
    +1
    Swagger/Swashbuckle

    И в связи с этим возникает вопрос: так чем же, все-таки, RAML принципиально лучше/удобнее?
      +2
      Чем лучше? Отвечаю по пунктам?

      1. Гораздо более простым и логичным синтаксисом описаний.
      2. Возможностью расписать в документации гораздо большее количество параметров, чем в том же swagger и, самое главное, сделать это при помощи более простых и удобных конструкций (сравните те же описания схем безопасности или query-параметров).
      3. Расширенными (по сравнению с тем же swagger) возможностями использования include'ов и наследования, благодаря чему документация получается более компактной и элегантной.

      Единственное, в чём RAML пока что проигрывает Swagger — недостаточное количество разрабатываемых сообществом инструментов. Но это — дело наживное.
        –1
        Для RAML есть инструменты, позволяющие автоматически генерить документацию в этом формате для уже существующего API (желательно — на лету)?
          +2
          Насколько мне известно, пока что нет.
            0
            Угу, потому что RAML — это «сначала контракт, потом все остальное». А я вот совершенно не хочу так писать, я хочу иметь (аннотированное) серверное API, по которому генерятся описания в произвольных форматах. И вот тут Swagger работает прекрасно (потому что для него есть соответствующий инструментарий под мою платформу).

            (хотя, конечно, в нем встречаются вещи, которые сложно выразить)
              +1
              Разные задачи — разные инструменты.
                +3
                Занятно, что при этом с точки зрения клиента задача-то одна.
                +2
                Т.е. вы не проектируете, а сразу пишете код?
                  +2
                  Почему же, я проектирую, просто код — это и есть проект. Code is the documentation, я не хочу думать о том, как содержать их в соответствии друг с другом.
                    +1
                    Согласен. Для проектов на JAX-RS использую maven-javadoc-plugin вместе с swagger-doclet, Он генерирует swagger-документацию по JavaDoc. Помимо обычных аннотаций используются специфические дополнительные аннотации, которые не мешают читать код, но помогают уточнить документацию. Дополнительный бонус — приучает писать полноценные комментарии :)
            +1
            Учитывая что эти форматы чаще употребляются сриптами чем читаются человеком (зачем это читать если можно посмотреть что в UI нагенерили для каждого endpoint) то логичность синтаксиса — это вопрос второй.

            А вот коммунити — это наживается непросто. За RAML стоит одна компания, как и за API Blueprint. Swagger старается вовлечь сообщество в стандарт.

            Ну и набирает обороты он быстрее и дальше это будет только ускоряться, как снежный ком.
            Например и Microsoft (Azure API Management, API Apps) и Amazon ( AWS API Management import) используют Swagger, а не RAML или API Blueprint.

            Для RAML есть генераторы клиентов, как например AutoRest для .NET?
              0
              >>>>Учитывая что эти форматы чаще употребляются сриптами чем читаются человеком (зачем это читать если можно посмотреть что в UI нагенерили для каждого endpoint) то логичность синтаксиса — это вопрос второй. >>>>

              Даже для скриптов лучше писать в простом формате и без лишних «костылей».

              >>>>Для RAML есть генераторы клиентов, как например AutoRest для .NET?>>>>
              Есть, но пока что они находятся в очень «сыром» состоянии.

              >>>>А вот коммунити — это наживается непросто. За RAML стоит одна компания, как и за API Blueprint. Swagger старается вовлечь сообщество в стандарт.>>>>>

              За RAML стоит не одна компания. В разработке спецификаций принимали участие представители Cisco, PayPal, BoxInc. Насчёт коммьюнити — оно уже формируется (см., например, здесь: raml.org/projects.html)
                +2
                Даже для скриптов лучше писать в простом формате и без лишних «костылей».

                Вот только скриптам совершенно все равно на, скажем, дублирование, и поэтому наследование и трейты им уже не так нужны. А если исходить из того, что описание и генерится автоматически — тогда и вопрос противоречивости не встает.
          0
          reStructuredText, как inline-документация, так и документация в целом.
            +2
            активно использую формат blueprint, всё, что описано в статье для RAML есть и в блупринте. Результат получается красивый и для пользователя удобный, но вот для писателя формат api blueprint немного корявенький, очень неудобно, например, следовать через чур строгим требованиям по количеству пробелов и табуляций: список параметров — значит два таба, ещё что-то — четыре таба и т.д. Это кажется немного излишним, ведь распарсить явно и так всё получится.
              +1
              У нас на PHP проекте API описано на WSDL, это описание работает и для первичной валидации. Документацию получаем с помощью XSLT преобразования WSDL-ки. Подход хорошо себя зарекомендовал. Но приведенный в статье набор инструментов вокруг RAML впечатляет.
                0
                А у вас REST API или SOAP API?
                  0
                  Именно REST
                    +2
                    А как вы в wsdl описали rest-ресурсы и, особенно, http-семантику?
                      0
                      Пример, входные параметры:
                       <xs:element name="accountRequestData">
                          <xs:complexType>
                              <xs:sequence>
                                  <xs:annotation>
                                      <xs:documentation source="id" xml:lang="ru">Идентификатор записи</xs:documentation>
                                      <xs:documentation source="inn" xml:lang="ru">ИНН</xs:documentation>
                                      <xs:documentation source="is_active" xml:lang="ru">Счет активен</xs:documentation>
                                      <xs:documentation source="user_id" xml:lang="ru">Пользователь</xs:documentation>
                                  </xs:annotation>
                                  <xs:element name="id" type="xs:int" />
                                  <xs:element name="inn" type="xs:string" minOccurs="0" />
                                  <xs:element name="is_active" type="ns:BOOL" />
                                  <xs:element name="user_id" type="xs:int" />
                              </xs:sequence>
                          </xs:complexType>
                      </xs:element>
                      


                      Пример части с документацией:
                      <operation name="methodAccount">
                          <documentation type="map">
                              <name>Вставка и изменение счета</name>
                              <status>works</status>
                              <speed_level>fast</speed_level>
                              <quota_per_second>20</quota_per_second>
                              <rest call="account" httpMethod="POST"/>
                          </documentation>
                      </operation>
                      
                        0
                        Это ваше собственное расширение или какой-то публичный фреймворк?
                          0
                          Фрейморк-то Yii2, но он тут ни при чем. Работа с WSDL через \DOMDocument.
                            0
                            Ну то есть ваше собственное расширение.
                              0
                              Методы для работы с \DOMDocument, кстати, вынесены в отдельную библиотеку, или скорее даже фреймворк, его у нас соседняя команда сделала, но он к сожалению не публичный.
                                +1
                                Мне достаточно того, что это ваше расширение, со всеми (отсутствующими) перспективами стандартизации и широкого распространения.

                                (btw, на глаз в нем есть ощутимое количество ошибок и неполноты, но, поскольку он ваш внутренний, это не принципиально)
                            0
                            Слишком сложно…

                            Кстати, а существуют ли какие-нибудь генераторы интерактивной документации для WSDL?
                              0
                              Не знаю про интеграктивную документацию, а вот клиенты на основании wsdl строятся, в том числе и песочницы.
                                0
                                Не сказал бы, что слишком сложно. По сути, за валидацию отвечает несколько строк. Если упрощенно, то примерно так:
                                $validator = new \DOMDocument( '1.0', 'UTF-8' );
                                $validator->loadXML( $xml );
                                if (!$validator->schemaValidateSource($xsd)) {
                                  ...
                                }
                                

                                Т.е. в PHP для этого готовые инструменты есть.
                                Или громоздко описания выглядят? Ну, возможно. Меня не напрягает, думаю, дело привычки.
                                Автоматические генераторы есть, и тут уже больше философский вопрос, что первично — документация или код. Мне близок подход, когда сперва составляется осмысленная документация.
                                  0
                                  А если у вас данные не в xml гуляют?
                                    0
                                    Бывает и такое. В этом случае просто JSON преобразуем в XML и направляем на валидацию
                                      0
                                      И вас не смущает, что вы в этом случае требуете, чтобы JSON имел конкретную последовательность элементов, чего он гарантировать не может и не будет?

                                      (ну и вообще это не вполне однозначное преобразование, если вы только не накладываете на xml и xsd дополнительных ограничений)
                                        +1
                                        Не смущает, на этот случай был придуман велосипед — переданные параметры сортируются в алфавитном порядке. В WSDL тоже параметры описаны в алфавитном порядке. Но, как оказалось, на этот случай есть готовое простое решение — вместо sequence можно использовать all в WSDL.
                                        Некоторые ограничения на формат есть, но по практике скажу, что трудностей это не создает.
                                    0
                                    Да, сами описания выглядят несколько громоздко, что немного напрягает.

                                    >>>Автоматические генераторы есть, и тут уже больше философский вопрос, что первично — документация или код. Мне близок подход, когда сперва составляется осмысленная документация.>>>>

                                    Я ничего не имею против этого подхода. Задавая свой вопрос, я никаких философских аспектов затрагивать не хотел. Мне действительно интересно, насколько сейчас распространён WSDL, как он поддерживается сообществом, какие есть вспомогательные инструменты.
                                0
                                Присоединяюсь к вопросу.
                            +1
                            Привет бывшему коллеге, все верно — в тот период, когда API, в «организации», в которой мы трудились, разрабатывался о RAML-e не могло быть и речи, а swagger знали немногие. Помню даже, что написал авто-генератор, но под него надо было в бд описывать/складывать сущности, а так как всем было лень — никто его так и не заюзал =)

                            Сейчас прогресс ушел вперед — есть не только форматы описания, но и форматы вывода данных — JSON API например, откровенно говоря, устав от многолетних наблюдений вело-костылей — решил запрогать авто-генератор на основе RAML, с полной поддержкой JSON API (можно отключать) под Laravel5.x, в частности под laravel-module, если кому-нибудь интересно — https://github.com/RJAPI/raml-json-api (буду рад контрибам и предметной критике — развитие наше все).

                            Данный подход «сцепления» входных параметров на RAML-генератор и на выходе — JSON API сущностей, отлично прижился, в крупной компании (~1500чел.), в которой работаю сейчас, разрабатывали с 3-мя коллегами (программистами-инженерами), но тут пилили под Yii2.
                          +2
                          Вообще RAML спецификация как и инструменты для работы с ним, мягко говоря, не особо обновляются в последнее время (около года).
                          Его удобно использовать, чтобы быстро спроектировать REST API и окинуть взглядом как все вместе выглядит.

                          Из того что не хватает в версии 0.8:
                          1. Поддержка массивов: some_path/1,2,3,5.json Никак это нельзя описать, только если отдельное описание вставлять.
                          2. Поддерживает только '=' в query string. Т.е. нельзя описать что-то типа: path?star<2015-08-27T08:30:00.000Z

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое