Hatena::ブログ(Diary)

winplusの日記 このページをアンテナに追加 RSSフィード

2012-07-03

Web API Design(その3)

これno titleの勝手訳のつづき。すべてをリソースとして扱うという原則のROAにたいして、リソースなしのレスポンスをどうするか、ときます。(その2)の「関連づけを単純化する」というセクションもふくめて、参考:URLに関する議論 -- なぜ僕はクエリパラメータを擁護、ときに推奨するのか - 檜山正幸のキマイラ飼育記 (はてなBlog)、それからいかにして合意と伝達の負担を減らすか: URLを例として - 檜山正幸のキマイラ飼育記 (はてなBlog)

ページ送りと部分的なレスポンス

部分的なレスポンスにより、開発者に必要としている情報だけを提供することができます。

Twitter APIのツイート用のリクエストを例にしましょう。ふつうのTwitterアプリケーションがたいてい必要としているものよりも、はるか大量のものを手に入ります。個人の名前、ツイートしたテキスト、タイムスタンプ、そのメッセージがリツイートされた回数、それにたくさんのメタデータが含まれるのです。

レスポンスで、開発者が必要としているものだけを提供するように対応している主要なAPIをいくつか - 部分的なレスポンスというアイデアを開拓したGoogleを含めて - みてみましょう。

LinkedIn
/people:(id,first-name,last-name,industry)

このリクエストは個人のID、名(first name)、姓(last name)、業種(industry)を返却します。

LinkedInは:(...)という簡約構文をつかって、必要な部分を選択します。この構文は見慣れない上に、開発者がサーチエンジンをつかって解析するのに向いていません。

Facebook
/joe.smith/friends?fields=id,name,picture
Google
?fields=title,media:group(media:thumbnail)

GoogleFacebook は同じようなアプローチで、これはうまく動作します。

それぞれfieldsというオプションパラメーターがあり、開発者は返却してほしいフィールドの名前をその後ろに置きます。

また、この例のように、リクエスト対象のリソースとは異なったリソースから別の情報をもってくるために、レスポンス中にサブ-オブジェクトをおくことができます。

カンマ区切りの一覧をとるオプションのフィールドを追加する

Googleのアプローチはとてもうまく機能します。

ここで、このアプローチをつかって犬(dogs)のAPIから、必要としている情報だけを手に入れる方法を示します。

/dogs?fields=name,color,location

単純に見ての通りです。開発者はアプリケーションが同時に必要とする情報だけを選択することができます。これは帯域幅の問題 - モバイルアプリケーションでは重要です - を小さくします。

関連するリソースをあわせて取得することで、必要な情報を手に入れるために必要なリクエストの数を減らすことができます。

データベース中のものを、簡単に開発者がページ送りできるようにする

データベースにある全部のリソースを返却するのは、ほとんどの場合、まずいアイデアです。

FacebookTwitter、そしてLinkedInがページ送りをどうしているか、見てみましょう。Facebook は相対位置(offset)と範囲(limit)を使っています。Twitterはページ(page)と1ページあたりのレコード数(rpp - records per page)を使っています。LinkedInは開始(start)とカウント(count)を使っています。意味的には、FacebookとLinkedInとは同じことをしています。

各システムで50から75までのレコードを取得するためには、以下を使うことになります。

  • Facebook - offset 50 and limit 25
  • Twitter - page 3 and rpp 25 (records per page)
  • LinkedIn - start 50 and count 25
相対位置(offset)と範囲(limit)を使う

私たちは相対位置(offset)と範囲(limit)を推奨します。より一般的ですし、主要なデータベースはうまく理解してくれますし、開発者にとっても扱いやすいでしょう。

/dogs?limit=25&offset=50

メタデータ

また、ページ送りされたそれぞれのレスポンスにメタデータ - 開発者にたいして有効なレコードの総数を示すもの - を含めることを推奨します。

既定値をどうするか

ゆるい経験則ですが、既定のページ送りはoffset=0のlimit=10です。

(limit=10&offset=0)

ページ送りの既定値はもちろん、データの大きさによります。リソースが大きければ、たぶん10個よりも少ない数に制限することを望むでしょう。もし小さければ、より大きな制限値を選ぶことは間違っていません。

まとめると:

カンマ区切りの一覧をとるオプションのフィールドを追加して、部分的なレスポンスをサポートしてください。開発者が簡単にページ送りができるよう、相対位置(offset)と範囲(limit)を使ってください。

リソースなしのレスポンスをどうするか

そもそもリソースではないレスポンスを送信するAPI呼び出しは、事業領域によっては珍しくありません。金融サービス、通信会社、自動車分野といった事業領域で、ある程度みてきました。次のような動作は、「リソース」のレスポンスをとして扱わないほうがよいこことの手がかりになります。

・計算(Calculate)

・移行(Translate)

・変換(Convert)

たとえば、つぎのようなことを行いたいとしましょう。いくら税金を払えばよいかといった単純なアルゴリズムの計算(Calculate)したり、あるいは自然言語を翻訳(Translate)(ある言語によるリクエストを、べつの言語によるレスポンスへ)したり、あるいはある通貨をべつのものへ変換(Convert)りたり、といったことです。データベースから返されるリソースは存在しません。

このような場合:

名詞でなく動詞をつかう

たとえば、100ユーロを中国人民元に変換するためのAPIがあります。

/convert?from=EUR&to=CNY&amount=100

APIのドキュメントで、これら「非-リソース」のシナリオが他と異なっていることを明記してください。

わかりやすくドキュメントのセクションを分けてください。そのことで、これと同様のケース - リソースをそのまま返却するというよりもレスポンスを生成あるいは計算する動作のもの - では動詞を使っていることが明確になります。

複数の形式をサポートすること

私たちは、複数のフォーマットをサポートすることを推奨します - 送信内容はひとつのフォーマットに詰め込まれますが、そのフォーマットは必要な数だけ受け入れましょう。ふつう、あるフォーマットから別のフォーマットへの対応付けは自動的化できます。

構文が似ているいくつかのAPIを挙げます。

Google Data
?alt=json
Foursquare
/venue.json
Digg*
Accept: application/json
?type=json

 *type引数は、Acceptヘッダーを上書きします。

Diggは2通りのやり方で指定することができあます。Acceptヘッダーをつかった純粋なRESTfulなやり方と、URLのなかのtypeパラメーターを使うやり方とです。これは混乱することがあります。最低限、矛盾があったときに何が起こるのかを文書化する必要があります。

わたしたちはFoursquareのアプローチを推奨します。

コレクションあるいは特定の要素をJSONフォーマットで手に入れる:

dogs.json

/dogs/1234.json

開発者あるいは通りいっぺんの利用者でさえ、どんなファイルシステムをつかっているとしても、ドット記法には馴染んでいます。また、言いたいことを伝えるために必要なのは、ひとつの文字(ピリオド)を追加することだけです。

既定のフォーマットをどうするか

私見ですが、JSONが既定のフォーマットとして勝ち抜きました。いまあるもののなかで、JSONがもっとも普遍言語に近いのです。たとえ、バックエンドが、Ruby on RailsPHPJavaPythonその他で構築されていたとしても、ほとんどのプロジェクトでは、フロントエンドは、JavaScriptに手を出しているでしょう。また、構文が簡潔だ - XMLよりも冗長でない - という利点もあります。

トラックバック - http://d.hatena.ne.jp/winplus/20120703/1341325758