開発効率を上げるWebAPIドキュメントの書き方


個人的にAPIサーバーを書くことが多く、APIクライアントの担当エンジニアと作業をする上で、効率よく作業できるAPIドキュメントの書き方を探していました。
今のところAPI Blueprintというフォーマットがしっくり来ていて、実際に私達が運用しているやり方と合わせて紹介したいと思います。

やりたいこと

当初APIのドキュメントに困っていたこと、やりたかったことは以下の通りです。

  • 場所と時間に制約されず、API設計を非同期でレビューしたい
  • バージョン管理したい
  • 簡潔に書きたい
  • リッチテキストは嫌だ!

API Blueprintとは

ざっくりいうと、MarkdownでAPIドキュメントを書くためのフォーマットのことです。
また、オープンソースの周辺ツールが非常に強力で、API Blueprintで書かれているドキュメントを読み込んでAPIのモックサーバーを建てるものや、テストケースを作成するもの、専用のHTMLRendererなどがあります。
JSON Schemaで出力できるRendererもあるようです。

出力例

実際のドキュメントは以下のように書きます。
(本当はもっといろんな書き方があるのですが、ここでは独断と偏見で可読性高そうなフォーマットを選択しています。)

# Group User
ユーザー

## ユーザー [/user]

### 個人データ取得 [GET]

+ Response 200 (application/json)

    + Body

        ```json
        {
          "user": {
            "id": 123456789,
            "name": "the40san",
            "avater_id": 1,
            "rank": 123,
            "exp": 12345678
          }
        }
        ```

### ユーザー設定変更 [PATCH]

+ Attributes

    + name: `` (string, optional) - ユーザー名
    + avater_id: `` (string, optional) - アバターID

+ Request example ユーザー名変更 (application/json)

    + Body

        ```js
        {
          "name": "つよい"
        }
        ```
+ Request example アバターID変更 (application/json)

    + Body

        ```js
        {
          "avater_id": 2
        }
        ```

+ Response 200 (application/json)

    + Body

        ```json
        {
          "user": {
            "id": 123456789,
            "name": "the40san",
            "avater_id": 1,
            "rank": 123,
            "exp": 12345678
          }
        }
        ```

Renderer

Rendererに掛けるとこんな感じのドキュメントが出来上がります。
ここでは、Rendererにaglioを利用しています。

API Blueprintを使ったレビューの仕方

仕様変更があったり、考慮漏れがあったりするため、APIの設計は開発中に頻繁に変わります。
サーバーとAPIクライアント間で認識の齟齬があるとビルドが壊れてしまうので、そういったことがないように私たちはAPIの設計レビューをお互いの視点から行っています。
差分が見やすかったりCIと連携するのが簡単だったりするので、レビュー用のツールとしてGithubを利用しています。

レビューは、私たちは普段以下のような手順で行っています。

  1. リポジトリにAPI Blueprintのコミット作ってpush
  2. GithubにPullRequestを作成する
  3. サーバー、APIクライアントの両方で認識があっているか両者がレビューする
  4. レビューOKならマージして、CIでRendererにかけて共有できる場所にアップロード

書き方

API Blueprintの仕様書をもとに、簡単な書き方のまとめを書いてみました。

Metadata セクション

API BlueprintはMetadataセクションから始まります。

FORMAT: X-1A
HOST: https://awesome-gameserver.com

Metadataセクションには、API Blueprint自体のバージョンや、各ツール固有の情報を書くセクションです。
よくわからなければ上のようなフォーマットで問題ないです。
フォーマットは、コロン:で区切ったKey-Value形式です。

API Name & Description

上から読んで一番初めの見出し(Markdown上は#)がAPI全体に関する名前と説明を書くセクションです。

FORMAT: X-1A
HOST: https://awesome-gameserver.com

# Awesome Game API
Oi! This is no time for standin' around

Resource Group セクション

API Blueprintの最上位の見出しになります。
複数のAPIをまとめるグループだと考えてもらうと伝わりやすいと思います。

# Group PlayerData
Resource group of "PlayerData" !

フォーマットは、# Group <Identifier>となります。
2行目以降はdescriptionです。

Resource セクション

ROAにおけるResourceを定義するセクションです。

# Group PlayerData
Resource group of "PlayerData" !

## User [/user]
Resource of "User" !

フォーマットは、## <Identifier> [<URL template>]です。他のフォーマットもあります。
URL templateについてはRFC 6570のものです。

Resourceセクションには、配下に必ず1つ以上のActionセクションを含む必要(SHOULD)があります。
また、以下のものを含んでもよい(MAY)とされています。

  • URI Parameterセクション
  • Attributesセクション
  • Modelセクション

Action セクション

各リソースに対してのアクションを設定するセクションです。
Railsのcontrollerをイメージするとわかりやすいでしょうか。

# Group PlayerData
Resource group of "PlayerData" !

## User [/user]
Resource of "User" !

### Get User information [GET]

+ Response 200 (application/json)

    + Body

        ```json
        {
          "user": {
            "id": 123456789,
            "name": "the40san",
            "avater_id": 1,
            "rank": 123,
            "exp": 12345678
          }
        }
        ```

フォーマットは、### <identifier> [<HTTP request method>]です。他のフォーマットもあります。
HTTP request methodにはGETPOST等、HTTPメソッドを指定します。ここでは、GETを指定しています。

Action セクションには以下のルールがあります。

  • 配下に必ず1つのResponse セクションを含む必要(SHOULD)があります。また、Response セクションは複数含んでもよい(MAY)です。
  • URI Parameter セクションを1つ以上含んでも良い(MAY)です。
  • Attributes セクションを1つ以上含んでもよい(MAY)ですが、その場合、Request セクションを指定する必要(SHOULD)があります。

 

URI Parameter セクション

URIパラメータを箇条書きで定義することができるセクションです。

# Group PlayerData
Resource group of "PlayerData" !

## Character Collection [/characters{?limit}]
Resources of "Character" !

### Get my characters [GET]
+ Parameters
    + limit: `50` (number, optional) - Limit if you need.
    + Default: `100`

+ Response 200 (application/json)

   ...

フォーマットは以下のとおりです。

+ <parameter name>: `<example value>` (<type> | enum[<type>], required | optional) - <description>

    <additional description>

    + Default: `<default value>`

    + Members
        + `<enumeration value 1>`
        + `<enumeration value 2>`
        ...
        + `<enumeration value N>`
  • parameter name にはURIパラメータ名を指定します。
  • requiredとoptionalを省略した場合は、requiredが指定されます。
  • typeがenumの場合、Membersに取りうる値を列挙することが出来ます。

Attributes セクション

リクエストやレスポンスに利用するパラメータを予め指定しておくためのセクションです。

# Group PlayerData
Resource group of "PlayerData" !

## User [/user]
Resource of "User" !

### Modify user information [PUT]

+ Attributes
   + name (string) - The name of user
   + avater_id (number) - The id of user's avater

+ Request (application/json)

+ Request (application/yaml)

+ Response 200

フォーマットはMSONになります。
Attributes セクションに記載した内容を、Request セクションやResponse セクションに継承させることが出来ます。

Model セクション

任意のタイミングで呼び出すことができるデータ型を定義するためのセクションです。
Request セクションやResponse セクションで利用することが出来ます。

# Group PlayerData
Resource group of "PlayerData" !

## User [/user]
Resource of "User" !

+ Model (application/json)

    ```json
        {
          "user": {
            "id": 123456789,
            "name": "the40san",
            "avater_id": 1,
            "rank": 123,
            "exp": 12345678
          }
        }
    ```


### Get User information [GET]

+ Response 200 (application/json)

    [User][]

定義フォーマットは、 + Model <Media Type>の後に実際のデータを記述します。
呼び出しフォーマットは、[Resourceの<Identifier>][]です

Request セクション

リクエスト方法に関する定義をするためのセクションです。

# Group PlayerData
Resource group of "PlayerData" !

## User [/user]
Resource of "User" !

### Modify user information [PUT]
+ Request modify user's name of current session (application/json)

    + Body

        ```json
        {
          "user": {
            "name": "the40san"
          }
        }
        ```

+ Request modify user's avater of current session (application/json)

    + Body

        ```json
        {
          "user": {
            "avater_id": 1
          }
        }
        ```

+ Response 200 (application/json)

    + Body

        ```json
        {
          "user": {
            "id": 123456789,
            "name": "the40san",
            "avater_id": 1,
            "rank": 123,
            "exp": 12345678
          }
        }
        ```

フォーマットは、Request <identifier> (<Media Type>)です。

Response セクション

APIレスポンスに関する定義をするためのセクションです。

# Group PlayerData
Resource group of "PlayerData" !

## User [/user]
Resource of "User" !

### Modify user information [PUT]
+ Request modify user's name of current session (application/json)

    + Headers

        X-UnityVersion: X.X.X

    + Body

        ```json
        {
          "user": {
            "name": "the40san"
          }
        }
        ```

+ Response 200 (application/json)

    + Headers

        X-UnityVersion: X.X.X

    + Body

        ```json
        {
          "user": {
            "id": 123456789,
            "name": "the40san",
            "avater_id": 1,
            "rank": 123,
            "exp": 12345678
          }
        }
        ```

+ Response 400 (application/json)

    + Body

        ```json
        {
          "error": {
            "message": "There is an error."
          }
        }
        ```

フォーマットは、Response <HTTP status code> (<Media Type>)です。

まとめ

  • API Blueprintの利用例と仕様に関してのまとめでした。
  • Railsとの連携がもっと強くなると嬉しいな!