php 43 lines · 8 steps

Filtered, paginated JSON APIs in Laravel

An Eloquent query builder collects filters conditionally, then an API Resource shapes each row into clean JSON.

Explained by highlit
1class PostController extends Controller
2{
3 public function index(Request $request): AnonymousResourceCollection
4 {
5 $posts = Post::query()
6 ->with(['author', 'tags'])
7 ->withCount('comments')
8 ->when($request->filled('search'), function ($query) use ($request) {
9 $query->where('title', 'like', "%{$request->string('search')}%");
10 })
11 ->when($request->filled('tag'), function ($query) use ($request) {
12 $query->whereHas('tags', fn ($q) => $q->where('slug', $request->string('tag')));
13 })
14 ->latest('published_at')
15 ->paginate($request->integer('per_page', 15))
16 ->withQueryString();
17 
18 return PostResource::collection($posts);
19 }
20}
21 
22class PostResource extends JsonResource
23{
24 public function toArray(Request $request): array
25 {
26 return [
27 'id' => $this->id,
28 'title' => $this->title,
29 'slug' => $this->slug,
30 'excerpt' => Str::limit(strip_tags($this->body), 160),
31 'comments_count' => $this->comments_count,
32 'author' => [
33 'name' => $this->author->name,
34 'avatar_url' => $this->author->avatar_url,
35 ],
36 'tags' => $this->tags->pluck('name'),
37 'published_at' => $this->published_at?->toIso8601String(),
38 'links' => [
39 'self' => route('posts.show', $this->slug),
40 ],
41 ];
42 }
43}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Conditional query methods like when() let you build filters from request input without scattering if-statements.
  2. 2Eager loading with with() and withCount() prevents N+1 queries when the response touches related records.
  3. 3API Resources separate database shape from response shape, giving you a stable, deliberate JSON contract.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Filtered, paginated JSON APIs in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code