REST APIのデータ制御について(クエリ、ページネーション)
前回の続きになります。今回は、どのようにAPIで返されるデータ量を、制御していくのかを学んでいきます。
例えば、大きなデータセットを扱う場合、返されるデータの量を制限することは帯域幅の観点から重要です。またUI処理の観点からも、大量のデータセットを一度に全てを表示はできないため、重要なものになります。
データ量の制限だけでなく、その大量のデータセットを「ページング」するかスクロールするかも、考慮されます。
制限方法としては、データの「ページ」を作成し、レスポンスに含めるリソースのフィールドやプロパティを指定します。
最終的には、特定の値を照会したり、返されたデータをソートしたりする事もあります。
またDBを扱う際には、クエリが投げられます。クエリの結果を制限し、改ページを実行するには、次の2つの方法が組み合わされて実行されます。
- リクエストは、「ページ」番号、ページあたりの項目数の指定、または最初と最後の項目番号を指定(範囲内を特定)しながら、返すデータ範囲を指定します。
- 1を終えたら、「ページ5に20商品を渡す」または「商品の100から120を表示」といった処理を行います。
クエリー、フィルタリング、ページネーションは、すべてのサービスに必ずしも必要な機能ではありません。この動作はリソース固有のものであり、各エンドポイントが何を持っているかは、サービスとリソースのドキュメントに基づきます。
返されるデータを制限するには、次の2つをサポートする必要があります。
- Range header
→ 例:Range: <unit>=<range-start>-<range-end> - query-string parameters
→例:resources?offset=0&limit=25
Range headerでは、HTTP仕様に基づき、データbyteをリクエストしています。
例えば、次の範囲指定は0から24までの商品、25個が返ってくることを期待したものになります。データセットは0から始まることに、注意が必要です。
Range: items=0–24一方でクエリは、offsetが始まりを、limitが返ってくる数の最大値を指定しています。上のRange headerと同じ処理をクエリで表すと、次のようになります。
resources?offset=0&limit=25このクエリとRange header両方が、使われるのがベスト・プラクティスになります。しかし、その際の注意事項として、クエリはRange headerの指定を上回ることがあります。つまり、クエリ処理の方が優先されます。
serverは上2つの範囲指定をもったリクエストをもらった時に、次のような Content-Range を返します。
Content-Range: items 0-24/66// 上の処理を終えた後に、残りのitemをリクエストした場合、次のようになります
Content-Range: items 40-65/66// 全item数がわからない場合は、アスタリスクで表せます。
Content-Range: items 40-65/*
つまり、Range header、クエリストリング、serverの処理をまとめると下のようになります。
// Range header
Range: items=25-49// Query String
GET ...?offset=25&limit=25// this is the data with that server would return
Content-Range: 25-49/66// 更には、クエリに時間を表すものを、引数に渡すなどもあります。
GET http://www.example.com/remarks/home_timeline?after=<timestamp>&offset=0&limit=20
GET http://www.example.com/remarks/home_timeline?before=<timestamp>&offset=0&limit=20
上の場合、たとえRange headerを設定しなかったとしても、サービスがデフォルトでデータの最大数の引数を返す時、サーバーにContent-Rangeヘッダーを応答させて、クライアントに制限を通知させる必要があります。つまり、どんな時でも、制限処理(Content-Range)をサーバーに設定させるということになります。
Content-Range: 0-19/4125次は、フィルタリングについてです。 これは、先程のページ区切りや、制限と連携されて成り立つものになります。それぞれのクエリ文字列パラメータ、フィルタおよび並べ替えを使用して実行されます。
フィルタリング引数を、一つにするか複数にするかは、議論の余地があります。が、今回は、引数名の衝突の可能性を減らす為(設計の簡潔性を保つ為)にも、 1つの引数を使う方が好ましいものになります。server側も、その一つの引数を確認すれば、フィルダリングをするかしないかを確認できるので、server側の管理が楽になります。
では、具体的な例を見ていきます、次のクエリは、Denverに住む「Todd」という名前のユーザーに、リクエストをしたものになります。このユーザーは、「grand poobah」という肩書も持っています。
GET http://www.example.com/users?filter="name::todd|city::denver
|title::grand poobah”ダブルコロン( “::”)の区切り文字はプロパティ名と比較値を区切り、比較値にスペースを含めることで、区切り文字をサーバー上の値から簡単に解析することができます。
つまりは、ダブルコロン( “::”)は名前と値を区切り、垂直バー( “|”)はフィルタフレーズを区切るものということです。また、この名前/値のペアーは、サービスから返されるプロパティの名前と、一致させる必要があります。
因みに、一般的には、フィルタリングにおいて、大文字小文字は区別しません。また、クエリにwild-cardsを使う事も可能となっています。比較値には演算値を使うことも可能です。
フィルタリングには、ソート機能も持たすことがあります。
ソートの例は次のようなリクエストになります。
GET http://www.example.com/users?sort=last_name|first_name|-hire_date上の例の場合、
最後の名前(昇順), 最初の名前(昇順)、雇われ始めた日(降順)でデータを取ってきます。この場合でもプロパティ名と、payload内でserverによって返されるデータの名前とは、一致させる必要があります。
以上がHTTPリクエストにおける、フィルタリングのルールになります。
次は、APIのバージョン管理についてです。
