API設計

ページネーション ぺーじねーしょん

オフセットカーソル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 カーソル方式:どちらを選ぶか

ページネーション方式の選択ガイド オフセット方式 offset=N & limit=M ✅ 任意のページにジャンプ可能 ✅ 実装がシンプル ❌ 大量offsetで遅くなる ❌ データ更新でズレが生じる 📄 管理画面・検索結果 (ページ番号UIが必要な場合) カーソル方式 cursor=TOKEN & limit=M ✅ データ更新があっても安定 ✅ 大量データでも高速 ❌ 任意ページへのジャンプ不可 ❌ 実装がやや複雑 📱 SNSタイムライン・無限スクロール (リアルタイムデータ・大規模DB) 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

関連用語