RESTful API データモデリング

RESTful APIで効率的なデータ取得を実現する:Filtering, Sorting, Paginationの設計方法

Tags: RESTful API, データモデリング, フィルタリング, ソーティング, ページネーション, API設計

はじめに

RESTful APIは、Webサービス間でデータを連携させるための一般的なスタイルとして広く普及しています。基本的なリソースの取得、作成、更新、削除といった操作は比較的理解しやすいかもしれません。しかし、実際のアプリケーション開発では、クライアントが必要とするデータを柔軟かつ効率的に取得できるような設計が求められます。特に、大量のデータを扱う場合や、多様な表示要件がある場合には、単にリソース全体を返すだけでは不十分です。

本記事では、RESTful APIを通じてデータを効率的に取得するための一般的な手法である、フィルタリング (Filtering)、ソーティング (Sorting)、ページネーション (Pagination) に焦点を当て、それぞれの設計方法とデータモデリングにおける考慮事項について解説します。これらの手法を適切に取り入れることで、APIのパフォーマンスを向上させ、クライアント側の実装をシンプルに保ち、保守性の高いAPIを構築することを目指します。

なぜデータ取得の効率化が必要なのか

APIからデータを取得する際、常にリソース全体のリストを全フィールド含めて返すことは、多くのシナリオで非効率です。

これらの問題を解決するために、API設計者はクライアントが必要とするデータを、必要な形式で、必要な量だけ取得できる仕組みを提供する必要があります。

Filtering (フィルタリング)

フィルタリングは、リソースのリストの中から特定の条件に合致するものだけを絞り込んで取得する手法です。例えば、「アクティブなユーザーだけを取得したい」「特定のカテゴリに属する商品だけを取得したい」といった要求に応えるために使用します。

設計方法

RESTful APIでは、フィルタリング条件をクエリパラメータとして指定するのが一般的です。

例: ユーザーリストからアクティブなユーザーを絞り込む GET /users?status=active

例: 特定のカテゴリIDを持つ商品リストから、在庫が0より大きいものを絞り込む GET /products?category_id=123&stock_gt=0

複数の条件を組み合わせる場合は、それぞれの条件をクエリパラメータとして追加します。パラメータ名には、対象となるフィールド名と、適用する条件(例: _gt (greater than), _lt (less than), _like など)を組み合わせることで、意図を明確にすることができます。

データモデリングにおける考慮事項

Sorting (ソーティング)

ソーティングは、取得したリソースのリストを特定のフィールドの値に基づいて並べ替える手法です。例えば、「作成日時が新しい順にユーザーリストを取得したい」「価格が安い順に商品リストを取得したい」といった要求に応えるために使用します。

設計方法

ソーティングの基準も、クエリパラメータとして指定するのが一般的です。

例: 作成日時が新しい順にユーザーリストを取得する GET /users?sort=created_at:desc

例: 価格が安い順、かつ名前のアルファベット順に商品リストを取得する GET /products?sort=price:asc,name:asc

sort パラメータなどに、ソート対象のフィールド名とソート方向(asc 昇順, desc 降順)をコロンなどで区切って指定する方法がよく用いられます。複数のソート条件を指定する場合は、カンマなどで区切ります。

データモデリングにおける考慮事項

Pagination (ページネーション)

ページネーションは、大量のリソースリストを小さな塊(ページ)に分割して取得する手法です。これにより、一度に取得・処理するデータ量を限定し、パフォーマンスとリソース消費を抑えることができます。

設計方法

ページネーションにはいくつかの方式がありますが、ここでは代表的な2つの方式を紹介します。どちらの方式を採用するかは、データの性質やAPIの利用シーンによって判断します。

  1. Offset/Limit方式 (オフセット・リミット方式)

    • 取得開始位置(オフセット)と取得件数(リミット)を指定します。
    • クエリパラメータ例: ?offset=20&limit=10 (21件目から10件取得)
    • シンプルで分かりやすい方式ですが、オフセットが大きい場合にパフォーマンスが低下する可能性があります。また、取得中にデータが追加・削除されると、同じデータが重複したり、一部のデータがスキップされたりする「スキュー」が発生する可能性があります。
    • 実装例: SELECT * FROM items LIMIT limit OFFSET offset;
  2. Cursor方式 (カーソル方式)

    • 前回の取得結果の末尾を示すカーソル(特定のレコードのIDやタイムスタンプなど)を指定し、そのカーソル位置から次のページを取得します。
    • クエリパラメータ例: ?since_id=12345&limit=10 (IDが12345より大きいレコードを10件取得)または ?next_cursor=abcdefg (エンコードされたカーソル文字列を指定)
    • オフセット方式よりも一般的にパフォーマンスが高く、データの追加・削除によるスキューが発生しにくいという利点があります。リアルタイム性の高いデータや、無限スクロールのようなユースケースに適しています。
    • ただし、特定の位置へのジャンプ(例: 10ページ目へ移動)が難しくなる場合があります。
    • 実装例: SELECT * FROM items WHERE id > since_id ORDER BY id ASC LIMIT limit;

レスポンスの設計

ページネーションを実装する際には、データ本体だけでなく、ページネーションに関するメタデータもレスポンスに含めることが推奨されます。

例(JSON形式):

{
  "data": [
    // リソースのリスト
  ],
  "meta": {
    "pagination": {
      "total": 100,    // 総件数 (オプショナル)
      "count": 10,     // このページに含まれる件数
      "per_page": 10,  // 1ページあたりの件数
      "current_page": 3, // 現在のページ番号 (Offset/Limitの場合)
      "total_pages": 10, // 総ページ数 (Offset/Limitの場合)
      "next_cursor": "...", // 次のページを取得するためのカーソル (Cursor方式の場合)
      "prev_cursor": "...", // 前のページを取得するためのカーソル (Cursor方式の場合)
    }
  },
  "links": {
    "self": "/items?page=3&limit=10",
    "first": "/items?page=1&limit=10",
    "prev": "/items?page=2&limit=10",
    "next": "/items?page=4&limit=10",
    "last": "/items?page=10&limit=10"
  }
}

meta フィールドや links フィールドにページネーション関連の情報を含めることで、クライアントは全体の件数や、次のページ、前のページへの遷移方法を容易に知ることができます。HATEOASの考え方を取り入れる場合、links フィールドに次ページなどへのURLを含めることは非常に有効です。

データモデリングにおける考慮事項

これらの手法を組み合わせた設計

Filtering, Sorting, Paginationは組み合わせて使用することがほとんどです。

例: カテゴリIDが123で、価格が安い順に並べ替えた商品リストを、1ページあたり10件ずつ、3ページ目から取得する GET /products?category_id=123&sort=price:asc&offset=20&limit=10

これらのパラメータを組み合わせる際の注意点として、パラメータ名の命名規則を統一し、ドキュメントで明確に定義することが挙げられます。また、各パラメータが相互にどのように影響するか(例: フィルタリングされた結果に対してソートとページネーションが適用される)も明確にしておく必要があります。

まとめ

RESTful APIにおいて、Filtering, Sorting, Paginationは、クライアントがデータを効率的かつ柔軟に取得するために不可欠な機能です。これらの機能を適切に設計することで、APIのパフォーマンスを向上させ、クライアント側の実装を簡素化し、全体として保守性の高いシステムを構築することができます。

設計においては、以下の点を考慮することが重要です。

これらの手法を理解し、自身の設計に取り入れることで、より実践的で品質の高いRESTful APIを構築できるはずです。どのような手法を採用するかは、APIの目的や対象となるデータの特性、そしてクライアントの利用シナリオを十分に考慮して判断することが重要です。