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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Conditional query methods like when() let you build filters from request input without scattering if-statements.
- 2Eager loading with with() and withCount() prevents N+1 queries when the response touches related records.
- 3API Resources separate database shape from response shape, giving you a stable, deliberate JSON contract.
Related explainers
php
<?php namespace App\Console\Commands;
Releasing stale document locks in Laravel
artisan-command
transactions
row-locking
Intermediate
6 steps
php
<?php namespace App\Providers;
Subdomain multi-tenancy routing in Laravel
multi-tenancy
service-container
route-binding
Advanced
7 steps
ruby
module Paginatable extend ActiveSupport::Concern private
A reusable pagination concern in Rails
pagination
concerns
http-headers
Intermediate
8 steps
php
<?php namespace App\Broadcasting;
Authorizing presence channels in Laravel
broadcasting
authorization
presence-channels
Intermediate
3 steps
php
<?php namespace App\Http\Middleware;
Resolving the current team in Laravel middleware
middleware
multi-tenancy
cookies
Intermediate
8 steps
php
<?php namespace App\Http\Controllers;
Broadcasting typing indicators in Laravel
websockets
authorization
presence-channels
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/filtered-paginated-json-apis-in-laravel-explained-php-63db/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.