CUBE SUGAR CONTAINER

技術系のこと書きます。

API Blueprint で Web API の仕様を書く

Web API のドキュメンテーションって結構めんどくさい。 どういうフォーマットで書くか悩むし、書いていくと似たような内容を何回も繰り返すことになる。 ドキュメントとは別に開発用のモックも用意しなきゃいけなくて、それとの整合性が…ってな具合に悩みは尽きない。 そんなとき今回紹介する API Blueprint を使うとそういった悩みを減らすことができるかもしれない。

今回使う環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.3
BuildVersion:   15D21

API Blueprint

API Blueprint というのは Web API (特に RESTful API) の仕様を記述するための言語仕様を指している。 ただし、これは単なる Markdown で書かれた文章を Web API の仕様書として解釈するための仕様にすぎない。 これで Web API の仕様を書いておくと、そこからドキュメントとかモックサーバまで生成できちゃう優れものになっている。

API Blueprint | API Blueprint

処理系

API Blueprint はあくまで仕様なので、それを解釈する処理系は別にある。 今回は Node.js を使った実装の aglio と api-mock を使うことにする。 aglio はドキュメントを生成できて api-mock はモックサーバを作れる。 ポイントは、それらがひとつの API Blueprint で書かれたファイルから作れること。 これによってドキュメントとモックサーバの整合性に悩む必要がなくなる。

github.com

github.com

aglio と api-mock をインストールする

まずは Homebrew を使って Node.js をインストールする。

$ brew install node

インストールするとパッケージマネージャの npm コマンドが使えるようになる。 install サブコマンドでグローバルに aglio と api-mock をインストールしよう。

$ npm install -g aglio api-mock

これでシステムに aglio と api-mock がインストールされた。

$ npm -g list | grep aglio
├─┬ aglio@2.2.0
│ ├─┬ aglio-theme-olio@1.6.2
$ npm -g list | grep api-mock
├─┬ api-mock@0.3.2

同時に aglio コマンドと api-mock コマンドが使えるようになる。

$ aglio --version
aglio 2.2.0
olio 1.6.2
$ api-mock --version
api-mock v0.3.2

API Blueprint で書かれた仕様書を用意する

まずはすごくシンプルな API Blueprint で書かれた仕様書を書いてみる。 中身はただの Markdown だし、見るだけでどういう風に書くのかなんとなくの雰囲気はつかめると思う。 簡単に解説しておくと /message に GET メソッドを実行すると Content-Type が text/plain で “Hello, World!” というメッセージが返ってくる。

$ cat << 'EOF' > helloworld.md
# GET /message

+ Response 200 (text/plain)

        Hello, World!

EOF

ドキュメントを生成する

次に aglio コマンドで先ほど用意したファイルを HTML に変換してみる。

$ aglio -i helloworld.md -o helloworld.html

HTML ファイルができるのでブラウザで開いてみよう。

$ ls | grep html$
helloworld.html
$ open helloworld.html

すると、なんか良い感じの見た目で Web API のドキュメントができている! すばらしい!

f:id:momijiame:20160301234607p:plain

ちなみに内容を確認しながら編集するときは aglio コマンドに -s オプションをつけて実行するのがおすすめ。

$ aglio -i helloworld.md -s

こうすると aglio が Web サーバを起動して変換した静的な HTML をホストしてくれる。 もちろん元の .md ファイルに変更があったときはビルドし直して表示してくれるという便利機能付き。

$ open http://127.0.0.1:3000/

その他、つかえるオプションは aglio の公式ドキュメントを参照のこと。

https://github.com/danielgtaylor/aglio#executable

モックサーバを用意する

次はモックサーバを用意してみよう。 といっても事前準備は既にすべて終わっている。 やることは api-mock コマンドに先ほどの .md ファイルを渡すだけ。

$ api-mock helloworld.md
info:    Enabled Cross-Origin-Resource-Sharing (CORS)
info:       Allow-Origin: *
info:       Allow-Methods: GET, PUT, POST, PATCH, DELETE, TRACE, OPTIONS
info:       Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, Referer, Prefer
info:    Listening on port 3000

これで localhost:3000 でモックサーバが起動する。 試しに curl で叩いてみよう。

$ curl http://localhost:3000/message
Hello, World!

ばっちり。

書き方の例

ここからは API Blueprint をどう書けばいいかの例を示していく。 尚、元ネタについては API Blueprint の仕様書をあたってもらいたい。

API Blueprint Specification | API Blueprint

API の説明を書きたい

API の説明を書きたいときはセクションの合間にちょいちょいと書けばいい。

# GET /message

こんにちは世界!

+ Response 200 (text/plain)

        Hello, World!

API 自体に説明をつけたいときは HTTP のリクエストをブラケットの中に入れて、説明をその左側に書く。

# 挨拶する [GET /message]

+ Response 200 (text/plain)

        Hello, World!

JSON を扱いたい

ここまでの例では Content-Type が text/plain のものを扱ってきた。 最近の Web API なら表現には JSON を使うことが多いはず。 そのためには、まずは Response セクションの括弧内を application/json にする。 その上で Body セクションを JSON で書こう。

# GET /message

+ Response 200 (application/json)

        {
            "message": "Hello, World!"
        }

ちなみに Content-Body の内容は上記のように直書きしなくても Attribute セクションに書くこともできる。 というより、こちらのやり方がおすすめ。 「<変数名>: <サンプル値> (<型>) - <説明>」という具合に書く。

# 挨拶する [GET /message]

+ Response 200 (application/json)

    + Attribute
        + message: Hello, World! (string) - メッセージ

何故かというと、こうしておけば aglio が Content-Body と一緒に JSON Schema の定義も一緒に作ってくれるから。

f:id:momijiame:20160308201751p:plain

便利だよね。

メタデータを追加する

ちなみに API Blueprint はドキュメントの先頭がメタデータセクションという場所になっている。 メタデータには、例えば将来的な拡張などに備えてバージョンのような情報を記載する FORMAT などがある。 厳格に書くのであれば現在のバージョンである 1A を指定しておこう。 他には API を提供するサーバのホストを HOST として書くこともできるようだ。

FORMAT: 1A
HOST: http://localhost/

# 挨拶する [GET /message]

+ Response 200 (application/json)

    + Attribute
        + message: Hello, World! (string) - メッセージ

リクエストボディのあるメソッドを扱う

POST や PUT のようにリクエストに色々と情報が必要なメソッドには Request セクションを書く。 また、何らかのヘッダが必要なときは Headers セクションを追加する。

# ユーザを追加する [POST /users]

+ Request (application/json)

    + Attribute
        + name: Mr. World (string) - ユーザ名

+ Response 201 (application/json)

    + Headers

            Location: /users/1

    + Attribute
        + id: 1 (number) - ユーザ識別子
        + name: Mr. World (string) - ユーザ名

変数を含むリソースを扱う

これまでに扱ってきたのは、すべてリソース (要するに URI) が静的なものだった。 もしリソースの中に変数が登場するときは波括弧を使ってそれを表現する。 変数の内容については Parameters セクションに説明を書いていく。

# ユーザの情報を更新する [PUT /users/{id}]

+ Parameters
    + id: 1 (number) - ユーザの識別子

+ Request (application/json)

    + Attribute
        + name: Mr. World (string) - ユーザ名

+ Response 200 (application/json)

    + Attribute
        + id: 1 (number) - ユーザ識別子
        + name: Mr. World (string) - ユーザ名

上記はパス文字列だったけどクエリ文字列のときは波括弧の先頭に「?」をつける。

# ユーザの情報を更新する [PUT /users/{id}{?token}]

+ Parameters
    + id: 1 (number) - ユーザの識別子
    + token: 18ede970... (string) - CSRF トークン

+ Request (application/json)

    + Attribute
        + name: Mr. World (string) - ユーザ名

+ Response 200 (application/json)

    + Attribute
        + id: 1 (number) - ユーザ識別子
        + name: Mr. World (string) - ユーザ名

複数の API をリソースグループとしてまとめる

複数の API をグルーピングしたいときは # を使う。 例えばユーザを操作する一連の API をまとめてみよう。

# ユーザ関連 API

## ユーザを追加する [POST /users]

+ Request (application/json)

    + Attribute
        + name: Mr. World (string) - ユーザ名

+ Response 201 (application/json)

    + Headers

            Location: /users/1

    + Attribute
        + id: 1 (number) - ユーザ識別子
        + name: Mr. World (string) - ユーザ名

## ユーザの情報を更新する [PUT /users/{id}]

+ Parameters
    + id: 1 (number) - ユーザの識別子

+ Request (application/json)

    + Attribute
        + name: Mr. World (string) - ユーザ名

+ Response 200 (application/json)

    + Attribute
        + id: 1 (number) - ユーザ識別子
        + name: Mr. World (string) - ユーザ名

グループにリソースを紐付けて書くこともできる。 その場合は、個別の API には HTTP メソッドだけを書く。

# ユーザ関連 API [/users/{id}]

## ユーザを追加する [POST]

+ Request (application/json)

    + Attribute
        + name: Mr. World (string) - ユーザ名

+ Response 201 (application/json)

    + Headers

            Location: /users/1

    + Attribute
        + id: 1 (number) - ユーザ識別子
        + name: Mr. World (string) - ユーザ名

## ユーザの情報を更新する [PUT]

+ Parameters
    + id: 1 (number) - ユーザの識別子

+ Request (application/json)

    + Attribute
        + name: Mr. World (string) - ユーザ名

+ Response 200 (application/json)

    + Attribute
        + id: 1 (number) - ユーザ識別子
        + name: Mr. World (string) - ユーザ名

Attribute を変数にする

何度も Attribute に似たような内容を書くのが面倒くさいときは Data Structure セクションに分割した内容を参照できる。

# ユーザ関連 API [/users/{id}]

## ユーザを追加する [POST]

+ Request (application/json)

    + Attribute (User)

+ Response 201 (application/json)

    + Headers

            Location: /users/1

    + Attribute (UserWithID)

## ユーザの情報を更新する [PUT]

+ Parameters
    + id: 1 (number) - ユーザの識別子

+ Request (application/json)

    + Attribute (User)

+ Response 200 (application/json)

    + Attribute (UserWithID)

## Data Structure

### User

+ name: Mr. World (string) - ユーザ名

### UserWithID

+ id: 1 (number) - ユーザ識別子
+ name: Mr. World (string) - ユーザ名

これは Content-Body の内容が入れ子構造になっているときにも便利に使える

# ユーザ関連 API [/users/{id}]

## ユーザを取得する [GET]

+ Response 200 (application/json)

    + Attribute (Users)

## Data Structure

### Users

+ results (array[UserSummary]) - ユーザのサマリーを格納した配列

### UserSummary

+ id: 1 (number) - ユーザ識別子
+ name: Mr. World (string) - ユーザ名

とりあえず、これくらい覚えておけば大体の Web API は表現できるかな?