REST APIのデータ制御について(クエリ、ページネーション)

Tuyoshi Akiyama
Aug 27, 2017 · 6 min read

前回の続きになります。今回は、どのようにAPIで返されるデータ量を、制御していくのかを学んでいきます。

例えば、大きなデータセットを扱う場合、返されるデータの量を制限することは帯域幅の観点から重要です。またUI処理の観点からも、大量のデータセットを一度に全てを表示はできないため、重要なものになります。

データ量の制限だけでなく、その大量のデータセットを「ページング」するかスクロールするかも、考慮されます。

制限方法としては、データの「ページ」を作成し、レスポンスに含めるリソースのフィールドやプロパティを指定します。
最終的には、特定の値を照会したり、返されたデータをソートしたりする事もあります。

またDBを扱う際には、クエリが投げられます。クエリの結果を制限し、改ページを実行するには、次の2つの方法が組み合わされて実行されます。

  1. リクエストは、「ページ」番号、ページあたりの項目数の指定、または最初と最後の項目番号を指定(範囲内を特定)しながら、返すデータ範囲を指定します。
  2. 1を終えたら、「ページ5に20商品を渡す」または「商品の100から120を表示」といった処理を行います。

クエリー、フィルタリング、ページネーションは、すべてのサービスに必ずしも必要な機能ではありません。この動作はリソース固有のものであり、各エンドポイントが何を持っているかは、サービスとリソースのドキュメントに基づきます。


返されるデータを制限するには、次の2つをサポートする必要があります。

  1. Range header
    → 例:Range: <unit>=<range-start>-<range-end>
  2. 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のバージョン管理についてです。

)
Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade