HOME / BLOG
BLOG

Laravelでページネーション付きAPIを作る方法【Resource Collection】

デフォルト画像

LaravelでAPIを作るとき、一覧取得を配列でそのまま返すと、フロント側で「次のページがあるのか」「今何ページ目なのか」を判断しにくくなります。そこで使いたいのが paginate()Resource Collection の組み合わせです。

この記事では、記事一覧APIを例に、ページネーション付きJSONを返す実装を作ります。

ルート

<?php
use App\Http\Controllers\Api\ArticleController;
use Illuminate\Support\Facades\Route;

Route::get('/articles', [ArticleController::class, 'index']);

Controller

<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\ArticleResource;
use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{
    public function index(Request $request)
    {
        $perPage = min((int) $request->query('per_page', 10), 50);

        $articles = Article::query()
            ->where('is_published', true)
            ->latest('published_at')
            ->paginate($perPage)
            ->withQueryString();

        return ArticleResource::collection($articles);
    }
}

Resource

<?php
namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ArticleResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'slug' => $this->slug,
            'excerpt' => $this->excerpt,
            'published_at' => optional($this->published_at)->toISOString(),
        ];
    }
}

フロントエンドから取得する例

async function fetchArticles(page = 1) {
  const params = new URLSearchParams({
    page: String(page),
    per_page: "10",
  });

  const response = await fetch(`/api/articles?${params.toString()}`);

  if (!response.ok) {
    throw new Error("記事一覧の取得に失敗しました。");
  }

  return response.json();
}

const result = await fetchArticles(1);
console.log(result.data);
console.log(result.links.next);
console.log(result.meta.current_page);

返ってくるJSONの見方

キー意味
data現在ページに含まれる記事一覧
links前後ページや先頭、末尾ページへのURL
meta現在ページ、最終ページ、総件数などのページ情報

実務で気をつけること

  • per_page は上限を決めて、重いリクエストを防ぐ
  • 検索条件や並び順を維持したい場合は withQueryString() を使う
  • 件数が非常に多く無限スクロール中心なら cursorPaginate() も検討する
  • Resourceで返す項目を絞り、DBの内部構造をそのまま公開しない

あわせて読みたい

参考資料