3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

新システムWebAPI設計のために Web API: The Good Parts を読んだから読んだ結果をまとめてみる

Last updated at Posted at 2018-10-03

経緯

新しくWebAPIを設計するにあたって、何の道標もなく設計していくのは危険だと思ったので、「Web API: The Good Parts」を読んで設計をしています。
読んでなるほどと思ったことをここに書き出していきます。書き出した内容は新API設計の参考にしますし、今後の私のWebAPI設計の参考にもなると思います。

なんでこの書籍選んだの?

  • ネットでの評判上々
  • とりあえずオライリー選んどきゃ間違いないっしょ!

エンドポイントの設計とリクエストの形式

WebAPIにおけるエンドポイントとはURIのことです。

覚えやすく、どんな機能を持つURI なのかがひと目でわかるようにする

短く入力しやすいURI

× http://api.example.com/service/api/search
○ http://api.example.com/search

人間が読んで理解できるURI

× http://api.example.com/sv/u/
○ http://api.example.com/service/user/

× http://api.example.com/seihin/12345
○ http://api.example.com/products/12345

大文字小文字が混在していないURI

× http://api.example.com/Users/12345
○ http://api.example.com/users/12345

サーバ側のアーキテクチャが反映されていないURI

× http://api.example.com/cgi-bin/get_user.php?user=100
○ http://api.example.com/users/100

ルールが統一されたURI

× Bad

処理 URL
友達の情報の取得 http://api.example.com/friends?id=100
メッセージの投稿 http://api.example.com/friend/100/message

○ Good!

処理 URL
友達の情報の取得 http://api.example.com/friends/100
メッセージの投稿 http://api.example.com/friends/100/messages

HTTPメソッド

WebアプリケーションではGETとPOSTを使う通信が一般的だが、WebAPIではそれ以外のメソッドを利用するケースが多くなっている。

メソッド 説明
GET リソースの取得
POST リソースの新規登録
PUT 既存リソースの更新
DELETE リソースの削除
PATCH リソースの一部変更
HEAD リソースのメタ情報の取得

example

目的 メソッド URL
ユーザー一覧取得 GET http://api.example.com/v1/users
ユーザー新規登録 POST http://api.example.com/v1/users
特定ユーザー取得 GET http://api.example.com/v1/users/:id
ユーザー情報更新 PUT/PATCH http://api.example.com/v1/users/:id
ユーザー情報削除 DELETE http://api.example.com/v1/users/:id

その他注意点

複数形の名詞を利用する

× http://api.example.com/user/12345
○ http://api.example.com/users/12345

世界的に複数形のほうが適切であるとされているらしい。
でも単数形が使われている有名サービスもたくさんあるし、必須ではない。

利用する単語に気をつける

英語がネイティブではない場合、残念ながら適切な単語を選ぶのはなかなか難しい。
searchとfindとか。写真はpicture ではなくphoto のほうが使われています。
迷ったら様々なAPIを紹介しているこのサイト([ProgrammableWeb](http://www.
programmableweb.com/))を参考にするといいらしい。

スペースやエンコードを必要とする文字を使わない

× http://api.example.com/v1/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC/123
↑これ、何のAPIか一目でわかりますか?

単語をつなげる必要がある場合はハイフンを利用する

× http://api.example.com/v1/users/12345/profile_image
× http://api.example.com/v1/users/12345/profileImage
○ http://api.example.com/v1/users/12345/profile-image

必須ではない。ある程度好みでOK。

検索APIで追加表示するときの設計

相対位置でデータを取ってこないで絶対位置でデータを取得する

× 相対位置 先頭から数えて何番目から何番目のデータをください
○ 絶対位置 このIDより新しいデータを20件ください

何故なら絶対位置のほうがパフォーマンスに優れているから。そして、途中でデータが挿入されても正しいデータを取得できるから。

ログイン周りのAPI設計

OAuth2.0を使うべき。

一般に公開するAPIなのか、内部利用するAPIなのか

一般に公開するAPI

  • 出来るだけ多くの利用者が利用しやすいような汎用的な設計を行う

内部利用するAPI

  • 必ずしも汎用的に作る必要はない
  • 「1 スクリーン1API コール、1 セーブ1API コール」
  • すなわち、ひとつの画面を表示するためにコールするのが1 つのAPIで済むように、それに合わせたAPI を用意し、何らかのデータをサーバに保存する場合にも1 回のコールで済むように、それ向けのAPI を用意するのがよい

レスポンスデータの設計

データフォーマット

json必須

とりあえずjsonに対応しとけばOK。必要であればxmlやjsonpに対応する。
jsonp対応する場合はエラー処理が複雑っぽい。

レスポンスの内容をユーザーが選べるようにする

返却データが大きい場合は、レスポンス内容をユーザーに選択させる設計にするのが有効
予めレスポンスグループとして「small」「medium」「large」の3区分位作っといてユーザーに選択させるのも有効

例 http://api.exmample.com/v1/users/12345?fields=name,age
例 http://api.exmample.com/v1/users/12345?fields=medium

エンベローブ(封筒)は必要か

{
  "header": {
    "status": "success",
    "errorCode": 0,
  },
  "response": {
    ... 実際のデータ ...
  }
}

上記のようなheaderresponseというエンベローブを使う方式は非推奨。せっかくHTTPを使ってるんだから、処理に成功したか失敗したかはHTTPステータスコードで表現すべきである(後述)

データをフラットにするべきか

正直好み。しかし、こういう意味のない構造化はフラットにすべきである(DBの構造がそうなっているから、とか内部都合に左右されるべきでない)

{
  "id": 23245,
  "name": "Taro Yamada"
  "profile": {
    "birthday": 3456,
    "gender": "male",
    "languages": [ "ja", "en"]
  }
}



{
  "id": 23245,
  "name": "Taro Yamada"
  "birthday": 3456,
  "gender": "male",
  "languages": [ "ja", "en"]
}

配列で返すか、オブジェクトで返すか

配列で返すとjsonハイジャックというセキュリティ上のリスクがあるので、オブジェクトで返そう

配列でそのまま返す例

[
  {
    "id": 234342,
    "name": "Taro Tanaka",
    "profileIcon": "http://image.example.com/profile/234342.png"
  },
  {
    "id": 93734,
    "name": "Hanako Yamada",
    "profileIcon": "http://image.example.com/profile/93734.png"
  }
]

オブジェクトで包む例

{
  "friends": [
    {
      "id": 234342,
      "name": "Taro Tanaka",
      "profileIcon": "http://image.example.com/profile/234342.png"
    },
    {
      "id": 93734,
      "name": "Hanako Yamada",
      "profileIcon": "http://image.example.com/profile/93734.png"
    }
  ]
}

検索結果の続きがあるかをどう返すか

続きがあるよフラグを設けるとよい。例えば何かを10件ずつ表示する機能の場合は、APIでは11件取得して11件目があれば「続きがあるよフラグ」をtrueにして返すのが良い

{
  "friends": [
    {
      "id": 234342,
      "name": "Taro Tanaka",
      "profileIcon": "http://image.example.com/profile/234342.png"
    },
    {
      "id": 93734,
      "name": "Hanako Yamada",
      "profileIcon": "http://image.example.com/profile/93734.png"
    }
  ],
  hasNext: true
}

日付のフォーマット

RFC 3339を使おう。
例 2015-10-12T11:30:22+09:00

大きな整数の取り扱い

桁数の大きな数値はjavascriptで誤動作するので文字列で返すのが良い。

var data = JSON.parse( '{"id":462781738297483264\}' );
console.log(data.id); // 462781738297483260

レスポンスデータを設計するべきうえで漠然と考慮すべきこと

APIは内部で持っているDB のテーブル構造をそのまま反映する必要は無い。API のユースケースをよく考え、ユーザーが最もシンプルに扱うことができる設計を目指す。

エラーの表現

HTTPステータスコードでエラーを表現する

後述します。

詳細なエラー内容をレスポンスに含める

こんな感じで、以下はTwitterの例

{
  "errors":[
    {
      "message":"Bad Authentication data",
      "code":215
    }
  ]
}

HTTPの仕様

HTTPステータスコードにはちゃんと意味がある

ステータスコード 意味
100番台 情報
200番台 成功
300番台 リダイレクト
400番台 クライアントサイドに起因するエラー
500番台 サーバーサイドに起因するエラー

もっと詳しく(太字のは覚えてね)

ステータスコード 名前 意味
200 OK リクエストは成功した。
201 Created リクエストが成功し、新しいリソースが作られた
202 Accepted リクエストは成功した
204 No Content コンテンツなし
300 Multiple Choices 複数のリソースが存在する
301 Moved Permanently リソースは恒久的に移動した
302 Found リクエストしたリソースは一時的に移動している
303 See Other 他を参照
304 Not Modified 前回から更新されていない
307 Temporary Redirect リクエストしたリソースは一時的に移動している
400 Bad Request リクエストが正しくない
401 Unauthorized 認証が必要
403 Forbidden アクセスが禁止されている
404 Not Found 指定したリソースが見つからない
405 Method Not Allowed 指定されたメソッドは使うことができない
406 Not Acceptable Accept 関連のヘッダに受理できない内容が含まれている
408 Request Timeout リクエストが時間以内に完了しなかった
409 Conflict リソースが矛盾した
410 Gone 指定したリソースは消滅した
413 Request Entity Too Large リクエストボディが大きすぎる
414 Request-URI Too Long リクエストされたURI が長すぎる
415 Unsupported Media Type サポートしていないメディアタイプが指定された
429 Too Many Requests リクエスト回数が多すぎる
500 Internal Server Error サーバ側でエラーが発生した
503 Service Unavailable サーバが一時的に停止している

HTTPステータスコードを理解して正しく使ってレスポンスする

  • API のリクエストが成功した、つまりクライアントが意図した動作をサーバ側が完了できた場合は200 番台を返す。
  • リクエストに何らかの不備があってサーバ側で意図を理解できなかったり、リクエストは理解できたけれど実行できない場合などは400 番台を返す
  • サーバ側がエラーを起こした場合は通常のウェブアプリケーションと同様に500 を、メンテナンスや何らかの理由でサービスを停止している場合は503を返す。

200番台: 成功

  • 処理に成功したら200を返す。
  • 正しくはPOSTの場合は201を、DELETEメソッドの場合は204を返すのがベスト。
  • POSTメソッドでデータの作成に成功したら、作成されたデータをレスポンスする。
  • PUTメソッドでデータの更新に成功したら、更新されたデータをレスポンスする。
  • DELETEメソッドでデータの削除に成功したら、削除されたデータをレスポンスする。

400番台: クライアント側の問題

  • 「あなたが誰だかわからないよ」は401を返す。
  • 「あなたが誰だかはわかったけど、この操作はあなたには許可されてないよ」は403を返す。
  • 「そんなページないよ」は404を返す。
  • 他の400番台のエラーで表すことが出来ないエラーは400を返す。(入力パラメータ誤り等)

500番台: サーバー側の問題

  • サーバー側の処理で何か予期せぬことが起きたら500を返す。
  • 意図的にせよ、意図的でないにせよ、サーバーが応答しなくなったら503を返す。

独自のHTTPヘッダを定義する

HTTPヘッダを新しく定義する場合は頭に"X-"と接頭辞を付けて、次にサービスやアプリケーション、組織の名前を付けるのが一般的

例 X-AppliName-Session-Id

設計変更しやすいWebAPIを作る

APIをバージョンで管理する

一度公開したAPIは出来るだけ変更しない。もし機能追加や何らかの大きな変更が必要な場合は、新しいAPIを作るべきである。古い形式でアクセスしてきているクライアントに対しては、それまでと変わらないデータを返し、新しい形式でのアクセスには新しい形式のデータを返す。すなわち複数バージョンのAPIを提供する。


http://api.example.com/v1/users/123
http://api.example.com/v2/users/123
http://api.example.com/users/123?v=1
http://api.example.com/users/123?v=2

バージョンをURLで受け取るか、クエリ文字列で受け取るか

決まりはない。好み。有名サービスは前者が多数。
後者のデメリットはクエリ文字列が省略された場合、どのバージョンのAPIが動くのか一目でわからない。また、省略された場合に最新のAPIとして動くのか、古いAPIとして動くのか、エラーにするのか処理を入れるのも手間

バージョンを変える際の指針

  • 後方互換性を保つことが可能な変更は可能なかぎり同じバージョンで何とかする。
  • どうしても後方互換性を保ったまま修正を行うことが難しい変更を加えなければならないときにのみ、バージョンを上げるべき。

古いAPIバージョンの提供を終了する

広く一般に公開されたAPI の提供を終了する場合には、事前に終了日時をアナウンスして、それまでに対応してくれるように周知徹底しなければならない。

堅牢なWebAPIを作る

サーバーとクライアント間の情報の不正入手

こんなことに対応しておくこと

  • HTTPS化

悪意のあるアクセスへの対策

こんなことに対応しておくこと

  • XSS対策
  • XSRF対策
  • jsonハイジャック
  • リクエストパラメータの改ざん
  • リクエストの再送信

大量アクセスへの対策

こんなことに対応しておくこと

  • ユーザーごとのアクセスを制限する

まとめ

  • 「設計の美しいWebAPIは使いやすく、変更しやすく、頑強であり、恥ずかしくない」そう本書の中にありましたが、まさにその通りだと思います。私は実際、過去に古いWebAPIを改修した経験がありますが、そのAPIは保守しにくく修正しにくく、使いにくいと感じていました。どうすればそういった使いにくさだったり保守しにくさを改善できるか考えていましたが明確な答えは導き出せずにいました。この書籍を読むことである程度は答えが出せたと思います。今後のWebAPI設計の糧になったと感じます。
  • ただし誤字脱字が多かったです。数えてないけど自分が見つけただけで10か所は軽くあったように思います。一度読めば分かる誤字脱字がほとんどだったので残念でした。
  • WebAPIをこれから設計する人、もしくは設計した経験がある人も振り返りとして読んでみると参考になる書籍だと思います。
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?