Taylor Otwell endorses three-package stack for modern Laravel 13 APIs
Overview
Modern API development requires more than just returning JSON. It demands predictability and standardization. Combining the native

Prerequisites
To follow along, you should have a solid grasp of
Key Libraries & Tools
- Laravel 13: The framework providing nativeJSON:APIresource support.
- Spatie Query Builder: A package that handles complex filtering, sorting, and including relationships via URL parameters.
- Scramble: An automated documentation generator that producesOpenAPI3.1.0 specifications directly from your code.
Code Walkthrough
Converting a standard resource to a
namespace App\Http\Resources;
use Illuminate\Http\Resources\JsonApi\JsonApiResource;
class BookResource extends JsonApiResource
{
public function attributes($request): array
{
return [
'title' => $this->title,
'price' => $this->price,
'in_stock' => $this->in_stock,
];
}
public function relationships($request): array
{
return [
'author' => $this->author,
];
}
}
In the controller,
public function index()
{
$books = QueryBuilder::for(Book::class)
->allowedFilters(['title', 'in_stock', 'price'])
->allowedIncludes(['author'])
->get();
return BookResource::collection($books);
}
Syntax Notes
attributes wrapper. JsonApiResource. Note the usage of allowedFilters in the query builder; it prevents users from guessing column names that shouldn't be exposed.
Practical Examples
By utilizing GET /api/books?filter[price]=<45&sort=-price&include=author returns books cheaper than 45 units, sorted descending by price, with the author data included. This eliminates the need for writing dozens of conditional if($request->has(...)) statements in your controllers.
Tips & Gotchas
filter[name]=value, which might differ from standard query strings.