カーソルページネーション かーそるぺーじねーしょん
簡単に言うとこんな感じ!
本の「しおり」を使ってデータを分割して取得する方法だよ!「101件目から」みたいなページ番号ではなく、「このレコードの次から」っていう目印(カーソル)を使って続きを読み込むんだ。SNSのタイムラインを下にスクロールしたときに新しい投稿が続々出てくる、あれがまさにコレ!
カーソルページネーションとは
カーソルページネーション(Cursor Pagination)とは、APIでデータを分割して取得する際に、「どこまで取得したか」を示す目印(カーソル)をもとに次のデータを取得する手法です。従来の「○ページ目を取得」という方式ではなく、「このIDより後のデータ」という形で次の取得位置を指定します。
一般的なページネーションには「オフセット方式」と「カーソル方式」の2種類があります。オフセット方式は LIMIT 10 OFFSET 100 のようにデータベースに直接ページ番号を渡しますが、データ件数が多くなると処理が遅くなったり、取得中にデータが追加・削除されると表示がズレる問題があります。カーソル方式はこの問題を根本的に解決するために使われます。
SNSのタイムライン、チャットの履歴、ECサイトの商品一覧など、大量のデータをリアルタイムに近い状態で表示するサービスではカーソルページネーションが標準的な選択肢になっています。特にデータ量が数十万件・数百万件を超えるシステムや、データの追加・削除が頻繁に起きる環境では、その効果が顕著に表れます。
オフセット方式との比較
| 比較項目 | オフセット方式 | カーソル方式 |
|---|---|---|
| ページ指定の方法 | page=3 や offset=20 | cursor=abc123 などの目印 |
| ページ番号へのジャンプ | ✅ できる | ❌ 前から順番に辿る必要がある |
| データ追加・削除時の安定性 | ❌ ズレが発生しやすい | ✅ ズレが起きにくい |
| 大量データでのパフォーマンス | ❌ ページが深くなると遅い | ✅ 常に高速 |
| 実装の難易度 | 低い | やや高い |
| 向いているUI | ページ番号付きナビゲーション | 無限スクロール・「次へ」ボタン |
しおりのたとえで覚えよう
オフセット方式は「本の100ページを開いて」という指定。カーソル方式は「この文章の続きから読んで」という指定。本の途中でページが増えたり減ったりしても、しおりの位置は変わらないのがカーソル方式の強みです。
カーソル値の種類
| カーソルの種類 | 説明 | 例 |
|---|---|---|
| IDベース | レコードの一意なIDをそのまま使う | cursor=10482 |
| タイムスタンプベース | 最後に取得したレコードの日時 | cursor=2024-01-15T09:30:00Z |
| Base64エンコード | IDや複合キーをBase64化して不透明にする | cursor=eyJpZCI6MTA0ODJ9 |
| 複合カーソル | 複数カラムの組み合わせ(ソート対応) | cursor=MTAyLDEwNDgy |
歴史と背景
- 2000年代前半:Webアプリの黎明期。データ件数が少なく、オフセット方式のページネーションで十分だった
- 2000年代後半:SNS・UGC(ユーザー投稿コンテンツ)の普及でデータ量が爆増。オフセット方式の限界が顕在化
- 2010年前後:Twitterが無限スクロールを採用。カーソルベースのAPI(
max_id/since_idパラメータ)が注目される - 2012年:Facebookが Graph API を公開し、
after/beforeカーソルによるページネーションを標準化 - 2015年:Facebookが Relay(GraphQLクライアント)を公開。
edges/node/cursorを使った Connections仕様 が業界標準として広まる - 2018年以降:GitHub、Stripe、Shopifyなど主要なAPIがカーソルページネーションをデフォルトに採用。REST・GraphQL両方で普及
APIでの実装パターン
カーソルページネーションの具体的なAPIのやり取りを見てみましょう。
リクエスト例(REST API)
GET /api/posts?limit=10&cursor=eyJpZCI6MTA0ODJ9
レスポンス例(JSON)
{
"data": [
{ "id": 10483, "title": "投稿タイトル1" },
{ "id": 10484, "title": "投稿タイトル2" }
],
"pagination": {
"next_cursor": "eyJpZCI6MTA0ODR9",
"has_next_page": true,
"has_previous_page": true,
"prev_cursor": "eyJpZCI6MTA0ODJ9"
}
}
次のページを取得するには、next_cursor の値をそのまま次のリクエストの cursor パラメータに渡すだけです。
GraphQL Connections仕様
GraphQLではFacebook由来の「Connections仕様」が広く使われます。
query {
posts(first: 10, after: "eyJpZCI6MTA0ODJ9") {
edges {
cursor
node {
id
title
}
}
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
以下の図は、REST方式とGraphQL Connections方式のカーソルページネーションの対応関係を示します。
関連する規格・RFC
| 規格・RFC番号 | 内容 |
|---|---|
| RFC 5988 | Web Linking。Link ヘッダーによる next / prev ページリンクの標準仕様(後継はRFC 8288) |
| RFC 8288 | Web Linking(RFC 5988の更新版)。REST APIのページネーションで Link: <url>; rel="next" を使う際の根拠となる仕様 |
関連用語
- REST API — WebでデータをやりとりするAPIの設計スタイル
- GraphQL — 必要なデータだけを柔軟に取得できるAPIクエリ言語
- オフセットページネーション — ページ番号や件数のスキップでデータを分割取得する従来方式
- 無限スクロール — スクロールに連動してデータを自動追加読み込みするUI手法
- JSONレスポンス — APIのデータ交換に広く使われる軽量なデータ形式
- レート制限 — APIへのリクエスト頻度を制御する仕組み
- データベースインデックス — データの検索・取得を高速化するデータベースの仕組み