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 JSON:API support in Laravel 13 with specialized packages creates a robust ecosystem for building scalable, documented, and filterable interfaces. This setup ensures that front-end clients receive data in a consistent format while providing back-end developers with powerful tools to manage complex queries and documentation without manual overhead.

Prerequisites
To follow along, you should have a solid grasp of PHP and Laravel fundamentals. Familiarity with Eloquent ORM, RESTful API design, and Composer for package management is essential. Knowledge of the JSON:API specification will help you understand the structural requirements of the response data.
Key Libraries & Tools
- Laravel 13: The framework providing native JSON:API resource support.
- Spatie Query Builder: A package that handles complex filtering, sorting, and including relationships via URL parameters.
- Scramble: An automated documentation generator that produces OpenAPI 3.1.0 specifications directly from your code.
Code Walkthrough
Converting a standard resource to a JSON:API resource in Laravel 13 simplifies the class structure. Instead of manual arrays, you define attributes and relationships.
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, Spatie Query Builder intercepts the request to apply filters. We restrict which fields are queryable for security.
public function index()
{
$books = QueryBuilder::for(Book::class)
->allowedFilters(['title', 'in_stock', 'price'])
->allowedIncludes(['author'])
->get();
return BookResource::collection($books);
}
Syntax Notes
JSON:API requires IDs to be strings, even if they are integers in the database. Furthermore, all model fields must reside within an attributes wrapper. Laravel 13 handles this wrapping automatically when you extend 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 Spatie Query Builder, users can perform advanced queries directly through the URL. For instance, 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
Scramble generates documentation on the fly without an artisan command, meaning your docs are always in sync with your code. However, ensure you use proper type hinting and docblock comments for Scramble to accurately detect validation rules. When using filters, remember that Spatie Query Builder expects an array syntax like filter[name]=value, which might differ from standard query strings.
- JSON:API
- 22%· products
- Laravel 13
- 17%· products
- Spatie Query Builder
- 17%· products
- Scramble
- 13%· products
- Composer
- 4%· products
- Other topics
- 26%

Laravel 13 Demo: JSON:API + Spatie Query Builder + Scramble API Docs
WatchLaravel Daily // 13:48