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

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.

Taylor Otwell endorses three-package stack for modern Laravel 13 APIs
Laravel 13 Demo: JSON:API + Spatie Query Builder + Scramble API Docs

Prerequisites

To follow along, you should have a solid grasp of

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

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,

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

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

, 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

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.

3 min read