ページネーション ぺーじねーしょん
オフセットカーソルREST APIレスポンス分割リスト取得スクロール
ページネーションについて教えて
簡単に言うとこんな感じ!
大量のデータを一度に全部返すんじゃなくて、「1ページ目は20件」「2ページ目は次の20件」みたいに分割して渡す仕組みだよ!本の「ページをめくる」のと同じイメージで、APIでも検索結果でも使われてるんだ。
ページネーションとは
ページネーション(Pagination) とは、大量のデータを一定件数ずつ分割して取得・表示する仕組みのこと。ECサイトの検索結果や一覧画面の「1 2 3 … 次へ」というナビゲーション、あるいはSNSのタイムラインのように画面をスクロールすると続きが読み込まれる動作もページネーションの一種だ。
APIの文脈では、クライアント(アプリやフロントエンド)がサーバーに「何件目から何件ください」とリクエストを送り、サーバーは該当範囲のデータだけを返す。全件を一度に返してしまうとレスポンスが巨大になり、サーバーもクライアントも処理が重くなるため、パフォーマンスとUXの両面で欠かせない設計パターンだ。
システム発注の現場では「一覧APIはちゃんとページングに対応していますか?」と確認するだけで、将来的なデータ量増加への対応力を測ることができる。データが少ない開発初期は問題なくても、本番運用でレコードが数万件・数十万件になったときに全件取得APIは一気に破綻するからだ。
ページネーションの主な方式
| 方式 | 仕組み | メリット | デメリット |
|---|---|---|---|
| オフセット方式 | offset=20&limit=10(20件目から10件) | 実装が簡単・任意ページに飛べる | データが挿入・削除されるとズレる / 大offset時に遅い |
| カーソル方式 | cursor=abc123&limit=10(特定レコードの続き) | データが変動しても安定・高速 | 任意ページへのジャンプ不可 |
| キーセット方式 | last_id=500&limit=10(ID>500を10件) | カーソルと同様に高速・実装がシンプル | ソート条件が複数だと複雑 |
| ページ番号方式 | page=3&per_page=20(3ページ目) | ユーザーに馴染みやすい | 内部実装はオフセットと同じ問題を持つ |
覚え方:「オフセットは地図の座標、カーソルはしおり」
- オフセット方式 =「100件目から」と座標で指定。ページを飛ばせるが、その間にデータが増減すると「ズレ」が起きる
- カーソル方式 =「このしおりを挟んだ続きから」と指定。SNSのタイムラインや無限スクロールに向いている
レスポンスに含める典型的なフィールド
{
"data": [...],
"pagination": {
"total": 1523,
"page": 3,
"per_page": 20,
"next_cursor": "eyJpZCI6MTAwfQ=="
}
}
total(総件数)、next_cursor(次ページのカーソル)、has_next(次ページの有無)などをレスポンスに含めるのが一般的な設計だ。
歴史と背景
- 1990年代〜 :Webの黎明期から検索エンジンや掲示板サイトが「次のページ」リンクを実装。物理的なページをウェブに持ち込んだ
- 2000年代 :RDBMSの
LIMIT / OFFSET構文が普及し、オフセット方式が主流に。実装の容易さから広く採用された - 2010年代前半 :Twitter・FacebookなどのSNSが普及し、無限スクロール(Infinite Scroll) が台頭。カーソル方式へのニーズが高まる
- 2012年頃 :Twitter APIがカーソルベースのページネーションを採用。
max_idパラメータによるキーセット方式が開発者に広まる - 2015年〜 :GraphQLの登場とともに Relay Cursor Connections仕様 が提唱され、カーソルページネーションの標準的な設計が整理された
- 2020年代 :大規模データを扱うクラウドサービスではカーソル・キーセット方式が主流となり、オフセット方式のパフォーマンス問題が広く認知されるように
オフセット方式 vs カーソル方式:どちらを選ぶか
GraphQL Relay Connections仕様
GraphQLでは Relay Cursor Connections という標準仕様が広く使われている。edges(エッジ)とnode(ノード)でリストを表現し、pageInfoにカーソル情報を持つ構造だ。
query {
users(first: 10, after: "cursor_abc") {
edges {
cursor
node {
id
name
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
REST APIでの典型的なURL設計
# オフセット方式
GET /api/products?offset=40&limit=20
# ページ番号方式
GET /api/products?page=3&per_page=20
# カーソル方式
GET /api/timeline?cursor=eyJpZCI6NTAwfQ&limit=20
# キーセット方式
GET /api/items?after_id=500&limit=20