WebAPIでエラーをどう表現すべき?15のサービスを調査してみた

  • 2083
    いいね
  • 5
    コメント

2017-01-05 追記

2016年3月にエラーの標準形式RFC7807「Problem Details for HTTP APIs」が提案され、今日現在proposed standard(標準化への提唱)となっています。こちらも是非ご覧ください。


最近はREST APIを提供しているサービスが増えてきていますね!また公開されるAPIだけでなく、Microservicesなアーキテクチャを採用して、バックエンドがWeb APIで通信するケースも増えてきているように思います。

APIを使うときはあまり気にしたこともなかったですが、いざAPIを設計してみるとどんなインターフェイスがいいのか、どんな形式がいいのかといった疑問が次々と出てきます。

今回、僕の会社で開発しているテスト自動化サービスのShouldBeeのチームでも、Web APIの設計に関して、エラーをどのように表現したらいいかがトピックにあがりました。

そこで、他のサービスではどのように表現しているか参考にしたいと思い、今回調査してみることしました。今回、調査は @t-hiroyoshi に協力してもらいました。ありがとうございます!

調査したサービス

今回、Web APIについて調査したウェブサービスは、次の15のサービスです。ぱっと思いついた順番で調べていったので、特に順番には意味はありません。また、調査範囲も思いつきです。

  1. Github
  2. Facebook
  3. Heroku
  4. Toggl
  5. Yahoo!
  6. GREE
  7. Nike
  8. Twitter
  9. Google
  10. Twillio
  11. Foursquare
  12. Flickr
  13. Linkedin
  14. Philips Hue
  15. Qiita

それでは、それぞれのサービスがWeb APIでエラーをJSONでどう表現しているか、個々に見て行きましょう。

Github

Github Developer

{
   "message": "Validation Failed",
   "errors": [
     {
       "resource": "Issue",
       "field": "title",
       "code": "missing_field"
     }
   ]
 }
  • message: エラーメッセージ
  • errors: 送られたフィールドにエラーがあった場合に追加されます。
    • resource: 対象のリソース
    • field: 対象のフィールド
    • code: エラーコード

典型的なエラーはmessageだけなのでシンプルですが、errorsで複数エラーが返せるのと、詳細なエラーを表現することもできる構造になっています。

Facebook

Using the Graph API

     {
       "error": {
         "message": "Message describing the error", 
         "type": "OAuthException", 
         "code": 190,
         "error_subcode": 460,
         "error_user_title": "A title",
         "error_user_msg": "A message"
       }
     }
  • message: エラーメッセージ
  • type: エラーの型
  • code: エラーコード
  • error_subcode: エラーのサブコード
  • error_user_title: ユーザに直接表示できるメッセージで、翻訳もされるとのことです。ダイアログのタイトルなどに表示するという用途。
  • error_user_msg: これもユーザに直接表示できるメッセージで、翻訳もされるとのことです。

Facebookの特徴は、エンドユーザ向けメッセージが別についてくることです。しかもリクエストのロケールにあわせて翻訳までしてくれるので、アプリを作る側にとったら便利ですね。

Heroku

Platform API Reference | Heroku Dev Center

{
  "id":       "rate_limit",
  "message":  "Your account reached the API rate limit\nPlease wait a few minutes before making new requests",
  "url":      "https://devcenter.heroku.com/articles/platform-api-reference#rate-limits"
}
  • id: エラーコード
  • message: エラーメッセージ
  • url: エラーの詳細が記述されたページのURL

Herokuは極めてシンプルな形式ですが、エラーについての説明があるページのURLがついてくるのが特徴のひとつです。

Toggl

toggl_api_docs/reports.md at master · toggl/toggl_api_docs

  {
    "error": {
      "message":"We are sorry, this Error should never happen to you",
      "tip":"Please contact support@toggl.com with information on your request",
      "code":500
    }
  }
  • message: エラーメッセージ
  • tip: このエラーをどう対処したらいいかの説明
  • code: HTTPステータスコード

Togglの特徴は、エラーをどう対処したらいいか簡単なヒントが付いてくる点です。

Yahoo!

Yahoo!デベロッパーネットワーク:エラーメッセージおよびコード - Yahoo!デベロッパーネットワーク

{
  "Error" : {
    "Message" : "error message"
  }
}
  • Message: エラーメッセージ

Yahoo!のAPIではエラーメッセージだけを返すようになっているようです。

GREE

Error Codes - GREE Developer Center

{
   "code":1001,
   "message":"Message API (batch type) to same user is called 
             several times by official user in a certain period of time. Service unavailable.",
   "ref_url":"http:\/\/docs.developer.gree.net\/error.html"
}
  • code: エラーコード
  • message: エラーメッセージ
  • ref_url: エラーの詳細が記述されたページのURL

GREEのエラーレスポンスも、Herokuと同様にシンプルな形式ですが、エラーについての説明があるページのURLがついてくるのが特徴のひとつです。

Nike

API Error Codes

{
    "requestId": "-1712857370761229397",
    "errors": [
        {
            "code": 90,
            "message": "Invalid format: Start Date must follow format yyyy-mm-dd"
        },
        {
            "code": 100,
            "message": "Invalid format: End Date must follow format yyyy-mm-dd"
        },
        {
            "code": 110,
            "message": "Invalid Format: count must be greater than or equal to 1"
        }
    ]
}
  • requestId: リクエストID
  • code: エラーコード
  • message: エラーメッセージ

Nikeでは、リクエストIDがありこれは開発者がAPIプロバイダに問い合わせしたときに利用されるものと思われます。また、複数のエラーが返せるようになっているのも特徴です。

Twitter

Error Codes & Responses | Twitter Developers

{
  "errors": [
    {
      "message": "Sorry, that page does not exist",
      "code": 34
    }
  ]
}
  • message: エラーメッセージ
  • code: エラーコード

TwitterのAPIのエラーレスポンスは、エラーが配列になっていてエラーが複数返せるように設計されている点が特徴です。

Google

Standard Error Responses - DoubleClick Search API — Google Developers

{
  "error": {
    "errors": [
      {
        "domain": "global",
        "reason": "appNotConfigured",
        "message": "The app with id {appId} does not exist or is not properly configured as a Google Drive app."
      }
    ],
    "code": 403,
    "message": "The app with id {appId} does not exist or is not properly configured as a Google Drive app."
  }
}
  • domain: どの領域のエラーかを特定するためのもののようです。例えばYouTube領域固有のエラーであればyoutube.parameterなどとなるようです。
  • reason: エラーコード
  • message: エラーメッセージ
  • code: HTTPステータスコード

Googleの特徴は、様々なサービスを横断して統一された形式のJSONを使っている点です。そのためサービスを表すためにdomainフィールドが設けられています。また、複数のエラーを返せるようにerrorsフィールドは配列になっています。

Twillio

Twilio Cloud Communications - APIs for Voice, VoIP, and Text Messaging

{
    "status": 400,
    "message": "No to number is specified",
    "code": 21201,
    "more_info": "http:\/\/www.twilio.com\/docs\/errors\/21201"
}
  • status: HTTPステータスコード
  • message: エラーメッセージ
  • code エラーコード
  • more_info: エラーについて詳細が書いてあるページのリンク

codemore_infoはフィールド自体がない場合もあるとのことです。

Foursquare

Responses & Errors

{
    "meta": {
        "code": 400,
        "errorDetail": "Missing access credentials. See https://developer.foursquare.com/docs/oauth.html for details.",
        "errorType": "invalid_auth"
    },
    "response": {}
}
  • code: HTTPステータスコード
  • errorDetail: エラーメッセージ
  • errorType: エラーコード

Foursquareの特徴は、通常のレスポンスとエラーレスポンスで形式の違いがなく、エラーが発生したときはレスポンスのmetaキーにエラーが入ることです。

Flickr

Flickr Services

{
    "code": 96,
    "message": "Invalid signature",
    "stat": "fail"
}
  • code: エラーコード
  • message: エラーメッセージ
  • stat: そのリクエストが成功したか失敗したか

Flickrはシンプルな形式ですが、statがあるのが特徴です。

Linkedin

{
    "errorCode": 0,
    "message": "Unknown authentication scheme",
    "requestId": "PFB5T8NVLO",
    "status": 401,
    "timestamp": 1432283172233
}
  • errorCode: エラーコード
  • message: エラーメッセージ
  • requestId: リクエストID。
  • status: HTTPステータスコード。
  • timestamp: リクエストのタイムスタンプ。

LinkedInのエラーレスポンスは、リクエストIDとリクエストのタイムスタンプがあるのが特徴です。もし開発者から問い合わせがあったときにコミュニケーションしやすそうですね。ただ、リクエストIDなどはヘッダにあったほうがいいのかもとも思います。

Philips Hue

API Core concepts | Philips Hue API

[
  {
    "error": {
      "type": 2,
      "address": "/",
      "description": "body contains invalid json"
    }
  }
]
  • type: エラーコード
  • address: エラーがあったURL
  • description: エラーメッセージ

Qiita

Qiita API v2ドキュメント - Qiita:Developer - Qiita

{
  "message": "Not found",
  "type": "not_found"
}
  • message: エラーメッセージ
  • type: エラーコード

まとめ

エラー内容の比較

以上のサービスのAPIが提供している情報を表にまとめるとこのようになります。

今回調査した範囲では全てのサービスが、人が読んで理解できる形式のメッセージを必ずエラーに含めていました。

エラーコードはプログラムがそのエラーをハンドルしやすいように、intまたは英数字からなるstringでした。なお、エラーコードを返すAPIの中には、エラーコード一覧表を提供しているサービスとそうでないサービスがありました。クライアントとしてはエラーコードの一覧表があれば、適切なエラーハンドリングをプログラミングしておくことができるので、APIプロバイダが一覧表を公開しておくと良いと思います。

サービスの性質によっては複数エラーを返せたほうがいいかもしれません。クライアントがアプリで入力欄が複数あるUIでは、一度に複数のエラーを把握できたほうが、いいユーザ体験になることがあります。

フィールド名の比較

それぞれのサービスで使われているフィールド名をまとめたのが次の表です。

メッセージについては、多くのサービスがmessageを採用していました。エラーコードについては、codeが最も多く、typeと名付ける例もありました。複数エラーを返す可能性があるサービスでは、errorsと複数形のフィールド名になっています。ステータスコードについては、codeないしはstatusが使われていました。詳細URLを提供しているサービスは、今回の調査範囲では少数派でしたが、url, rel_url, more_infoが使われていました。

APIを設計するときは、他のサービスで使われているネーミングに倣っておくと、他のAPIの経験がある開発者とって、そのフィールドが何を表現しているのか理解しやすくなるのではないでしょうか?

最後に調査していて、APIドキュメントに、エラーレスポンスのボディについて説明を設けているサービスとそうでないサービスがあることに気づきました。開発者としては、APIがどのような形式のエラーを返すのかが分かりやすく書いてあると、クライアントを実装しやすいと思います。また、ドキュメントにはエラーに含まれるフィールド名のリストや表だけではなく、具体的にJSONの例もあると親切だと感じます。

エラーレスポンスで考慮したいこと

以上の調査を終えて、僕なりにエラーに表現するときに考慮したいことをまとめてみます。

  • エラーメッセージ: 開発者が読んで分かるメッセージを入れる。
  • エラーコード: クライアントのプログラムがエラーハンドリングの手がかりになる情報を与える。加えて、ドキュメントではエラーコードの一覧を公開する。
  • 複数エラー: サービスによってケースバイケースだが、複数のエラーが表現できるようになっている。
  • 詳細URL: 公開APIでドキュメントが整っているなら、開発者のググる手間がなくなる。
  • 一貫性: どのエンドポイントでも同じ構造のJSONになっている。
  • シンプル: 複雑な構造がなくネストが浅いJSONのほうがクライアントが複雑な型を定義しなくて済む。

おわり

Web APIでエラーをどのように表現したらいいか、15のサービスの例を見ながら考えてみました。どのサービスでも共通する部分がある一方、独自の工夫がこらされているAPIもありました。Web APIを実装する機会があればぜひ参考にしてみてください。

関連シリーズ