مقاله

paginate، per_page، sort، filter و Spatie Query Builder — لیست API مقیاس‌پذیر بدون query دستی در Controller.

Pagination و Filtering در API Laravel — query parameter استاندارد

Pagination و Filtering در API Laravel — query parameter استاندارد

endpoint GET /posts بدون pagination یعنی دعوت به disaster — ۱۰۰هزار رکورد JSON و timeout. فیلتر و sort هم اگر whitelist نباشند، SQL injection و full table scan می‌آورند. این راهنما الگوی امن و استاندارد برای لیست API است.

Pagination پایه

$perPage = min($request->integer('per_page', 15), 100);

$posts = Post::query()
    ->published()
    ->with(['category'])
    ->latest('published_at')
    ->paginate($perPage);

return PostResource::collection($posts);

سقف per_page حتماً — کاربر نتواند per_page=10000 بزند.

Query parameters قرارداد

  • page — شماره صفحه
  • per_page — تعداد (با max)
  • sort — مثلاً -published_at (منفی = desc)
  • filter[category] — فیلتر
  • search — جستجوی متنی

Sort امن

$sort = $request->string('sort', '-published_at');
$direction = str_starts_with($sort, '-') ? 'desc' : 'asc';
$column = ltrim($sort, '-');

$allowed = ['published_at', 'title', 'views'];
if (! in_array($column, $allowed, true)) {
    $column = 'published_at';
}

$query->orderBy($column, $direction);

هرگز sort را مستقیم از user به query ندهید.

Filter ساده

if ($category = $request->string('filter.category')->toString()) {
    $query->whereHas('category', fn ($q) => $q->where('slug', $category));
}

if ($search = $request->string('search')->toString()) {
    $query->where(fn ($q) => $q
        ->where('title', 'like', "%{$search}%")
        ->orWhere('excerpt', 'like', "%{$search}%"));
}

برای فارسی fulltext بعداً Meilisearch — بهینه‌سازی.

Spatie Laravel Query Builder

use Spatie\QueryBuilder\QueryBuilder;

$posts = QueryBuilder::for(Post::class)
    ->allowedFilters(['title', 'status', AllowedFilter::exact('category.slug')])
    ->allowedSorts(['published_at', 'title'])
    ->allowedIncludes(['category', 'user'])
    ->defaultSort('-published_at')
    ->paginate($perPage);

whitelist built-in — توصیه برای API بزرگ.

Cursor pagination

Post::query()->orderBy('id')->cursorPaginate(20);

برای feed بی‌نهایت بهتر از offset در داده huge — performance.

Index دیتابیس

ستون‌های sort و filter index داشته باشند — composite مثل (status, published_at).

جمع‌بندی

همیشه paginate + whitelist sort/filter. Resource: API Resource. پایه API: REST API.

API لیستی سنگین