RESTfulなWeb APIを設計するときに考えること


ApigeeやHerokuのドキュメントに従うのでもよかったんだけど、読んでいく中でやっぱり考えることは出てきたので、あくまで自分のための指針をまとめてます。

包括的なWeb API作成についての知見
--------------------------------

* [HerokuのAPIデザイン | SOTA](http://deeeet.com/writing/2014/06/02/heroku-api-design/)
* [interagent/http-api-design · GitHub](https://github.com/interagent/http-api-design)
* [Web API 設計のベストプラクティス集 "Web API Design - Crafting Interfaces that Developers Love" - フリーフォーム フリークアウト](http://d.hatena.ne.jp/cou929_la/20130121/1358776754)
* [Web API Design | Apigee](http://apigee.com/about/resources/ebooks/web-api-design)

NetFlixもWeb APIの設計についての知見をよく放出してくれてる印象。

設計の基本
----------------

* 基本は `/コレクション/名詞`
* リソースの関係を表したい時に階層化する `members/1234/friends`
* 階層は浅く保つ
  * できるだけ/collection/identiﬁer/collection以上深くすべきでない
* URLは浅く保ち複雑さはクリエリパラメータに
* 動詞で表現してもいい `members/1234/notify`
* トランザクションもある種のリソース
* `/system/version`というAPIで稼働中のバージョンを返すのもあるとちょっと便利な場合もある
  * `/system/mode`というのも同様

原理主義的にやるとツライ。プラグマティカルなバランスをとろう。

* [Rest ful api設計入門](http://www.slideshare.net/MonstarLabInc/rest-ful-api)
* [RESTful Web アプリの設計レビューの話](http://www.slideshare.net/t_wada/restful-web-design-review)
* http://blogs.ricollab.jp/webtech/wp-content/uploads/2008/02/rest_and_roa.pdf
* [5.16. RESTful Web Service — TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.0.1.RELEASE documentation](http://terasolunaorg.github.io/guideline/5.0.1.RELEASE/ja/ArchitectureInDetail/REST.html)
* [リソースモデリングパターン](http://rest-pattern.hatenablog.com/)
* [URL設計で悩んでいます。 - QA@IT](http://qa.atmarkit.co.jp/q/2555) - 錚々たる面々が答えてて、かつ、それぞれの答えがみんな違っているのがとても参考になる。

バージョニング
--------------------

* 後方互換性を壊す場合にバージョンを上げる
  * IFが変わるときにバージョン上げるわけではないということ
  * 単純な拡張であればバージョンは上げない
    * 既存のAPIであっても拡張ならバージョン上げないし、エンドポイントの追加でもバージョン上げない
  * 内部実装が変わるだけなら（基本的には）バージョン上げない
  * ふるまいが保てない場合はバージョンを上げる

バージョン上げるタイミングはそれほど多いのだろうか。バージョンを上げなければいけないような破壊的変更が発生するのはドメインに対する検討が足りていないのではないか。とはいえ、僕はたいてい検討が足りないのでバージョン情報を入れておく。

### バージョンアップのユースケース例

例えばこんな感じにするというひとつの指針。

| ケース | バージョン上げる？ |
|-----------|------------|
| エンドポイントの追加 | バージョン上げない |
| エンドポイントの変更（非破壊的） | バージョン上げない |
| エンドポイントの変更（破壊的） | バージョン上げない<br> → 既存のエンドポイントをdeprecatedにして、変更後のエンドポイントを新規のものとして定義する。というのがいいと思うので、クエリーパラメーターでバージョンを渡すのはあまり使い道がないと思う |
| エンドポイントの削除 | バージョン上げる<br> → 現実解としてはdeprecatedにしてからあとでほかと合わせて整理がいいんじゃなかろうか |
| 不要、非推奨のエンドポイントが増えてきた。整理したい | バージョン上げて整理 |
| エンドポイントを大幅に作り直す | バージョン上げる、か、そこまでの大きな変更ならそもそもドメイン分けるとかになるのか？ |

### 表現方法

どのように表現するかで一長一短あるが、基本的にリソースパスで表現するのがリスク少ない。

あと、SemVerをそのままやるのはやり過ぎ。後方互換性を壊す場合にのみバージョンを上げればいいので、つまりはメジャーバージョンのみでよいということ。バージョン情報を渡したいのなら、レスポンスヘッダにつけるほうが良い。 e.g.) `X-Api-Version: 1.0.1`

#### リソースパス

    /v1/users/123

* ブラウザからの開発しやすい
  * ヘッダにしても、オプショナルにすればいいんじゃないの？
     * 筋悪
     * オプショナルにすると最新版がデフォルトにせざるを得なくなり、かつ、バージョン上げたときにバージョン上がったせいで動かなくなるクライアントが多数出てしまう
* よく使われている
  * 一長一短なのであれば普及しているものに乗っかるのも悪くない

#### HTTPリクエストヘッダー

##### Acceptsヘッダー

    Accept: application/json; version=3

または

    Accept: application/vnd.heroku+json; version=3

みたいな感じ。

##### 独自ヘッダ

    X-Api-Version: 1

Acceptsヘッダーでいいじゃないかと思う。

#### クエリーパラメーター

あんまりメリットに同意できない。

### 実装

継承でやるなり、mod_rewriteでやるなり、コピペしないで済む方法を採用すればなんでもいいかと思う。要検討。

    コントローラを分けても、モデル層やJSON生成層その他で発生した非互換な変更には無力

というのはその通りだと思う。

[APIのバージョニングは限局分岐でやるのが良い - Hidden in Plain Sight](http://kenn.hatenablog.com/entry/2014/03/06/105249)

### 参考

* [You don't need API version 2 - yohei's diary](http://yohei.hatenablog.com/entry/2014/03/12/001707)
* [Kazuho's Weblog: 拡張可能なWeb APIの設計原則と、バージョン番号を使う理由について](http://blog.kazuhooku.com/2014/03/web-api.html)
* [RESTfulなサービスのバージョンングから得られた知見](http://www.infoq.com/jp/news/2013/09/versioning-restful-services)


ドキュメントどうするか
----------------------

### 設計時

[API Blueprint](https://apiblueprint.org/)の作法にのっとってMarkdownで書いて[Apiary](https://apiary.io/)を使う。

* [REST APIドキュメント作成ツールはapiary.ioが決定版かもしれない - Qiita](http://qiita.com/horimislime/items/38327a3f4166b7b39eb5)
* [APIドキュメント作成ツールまとめ - dackdive's blog](http://dackdive.hateblo.jp/entry/2015/07/05/103000)

### 実装後

[Swagger](http://swagger.io/)を利用して実装から生成できるようにする。ドキュメントと実装が離れていると更新がしんどい。

* [Maven - JAX-RS+Glassfish+SwaggerでシンプルにはじめるAPIドキュメンテーション - Qiita](http://qiita.com/kazuhirokomoda/items/c6c373c0f7ad2331ed4a)