zukucode
主にWEB関連の情報を技術メモとして発信しています。

ASP.NET Coreで一覧APIにページングを付ける

商品が増えたあとも一覧APIが全件を返すと、転送量と画面の表示時間が増えます。最初はpagepageSizeを受け取るページングが分かりやすい方法です。

まず入力の上限を決める

[HttpGet]
public async Task<IActionResult> FindAll(
    [FromQuery] int page = 1,
    [FromQuery] int pageSize = 20,
    CancellationToken cancellationToken = default)
{
    if (page < 1 || pageSize < 1 || pageSize > 100)
        return BadRequest("pageは1以上、pageSizeは1〜100を指定してください。");

    var offset = ((long)page - 1) * pageSize;
    var items = await repository.FindPageAsync(pageSize, offset, cancellationToken);
    return Ok(new { Page = page, PageSize = pageSize, Items = items });
}

このActionは[ApiController]を付けたController内の例です。pageSizeに上限を置くのは、1回の要求で大量のデータを読み込ませないためです。計算をlongで行うとintの乗算による桁あふれを避けられますが、極端に大きいpageはAPIの要件に応じて別途拒否してください。

SQLでは順序を固定する

SELECT product_id, name, price
FROM products
ORDER BY product_id
LIMIT @limit OFFSET @offset;

@limit@offsetは、利用するDBライブラリでパラメータとして渡します。ORDER BYがないと取得順が保証されず、同じ商品が複数ページへ現れたり、抜けたりする原因になります。更新日時で並べる場合も、同じ日時の行の順序が安定するようORDER BY updated_at DESC, product_id DESCのように一意な列を最後へ加えます。

画面に「次がある」を伝える

件数の合計が不要なら、要求数より1件多く取得して次ページの有無を判断できます。たとえば20件表示するなら21件取得し、21件目があればhasNext = trueとして、画面へ返すのは先頭20件だけにします。

ページ間で商品が追加・削除されると、OFFSETの位置がずれます。大きな一覧や頻繁に更新される一覧では、最後に見たIDを次回の条件にするカーソル方式も検討します。最初から両方式を実装する必要はありませんが、OFFSETの限界は知っておきましょう。


関連記事