WebAPI

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

More than 3 years have passed since last update.

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


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

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


設計の基本


  • 基本は /コレクション/名詞

  • リソースの関係を表したい時に階層化する members/1234/friends

  • 階層は浅く保つ


    • できるだけ/collection/identifier/collection以上深くすべきでない



  • URLは浅く保ち複雑さはクリエリパラメータに

  • 動詞で表現してもいい members/1234/notify

  • トランザクションもある種のリソース


  • /system/versionというAPIで稼働中のバージョンを返すのもあるとちょっと便利な場合もある



    • /system/modeというのも同様



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


バージョニング


  • 後方互換性を壊す場合にバージョンを上げる


    • IFが変わるときにバージョン上げるわけではないということ

    • 単純な拡張であればバージョンは上げない

    • 既存のAPIであっても拡張ならバージョン上げないし、エンドポイントの追加でもバージョン上げない

    • 内部実装が変わるだけなら(基本的には)バージョン上げない

    • ふるまいが保てない場合はバージョンを上げる



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


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

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

ケース
バージョン上げる?

エンドポイントの追加
バージョン上げない

エンドポイントの変更(非破壊的)
バージョン上げない

エンドポイントの変更(破壊的)
バージョン上げない
→ 既存のエンドポイントをdeprecatedにして、変更後のエンドポイントを新規のものとして定義する。というのがいいと思うので、クエリーパラメーターでバージョンを渡すのはあまり使い道がないと思う

エンドポイントの削除
バージョン上げる
→ 現実解としては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


参考


ドキュメントどうするか


設計時

API Blueprintの作法にのっとってMarkdownで書いてApiaryを使う。


実装後

Swaggerを利用して実装から生成できるようにする。ドキュメントと実装が離れていると更新がしんどい。