2 min read

Stop Creating Multiple API Resources for the Same Model

Stop Creating Multiple API Resources for the Same Model

Building and maintaining APIs often leads to what I call "Resource Bloat." You start with a simple UserResource, but soon you need a slim version for a search dropdown, a medium one for a table, and a full version for the profile page.

Before you know it, your project is cluttered with UserLiteResourceUserListResource, and UserDetailResource. Or worse, your original resource is full of messy $this->when() logic that's hard to read.

Coming from a background with various frameworks, I always admired how Symfony handled this with "serialization groups." I wanted that same level of control in Laravel without losing the elegance of API Resources. That’s why I built Laravel Resource Scope.

The Problem: Resource Duplication

The goal was to keep a single source of truth for a resource but filter the output based on the context of the request. I didn't want to write new classes every time the frontend needed a slightly different payload.

The package allows you to define these contexts—or "scopes"—directly in your resource using a simple scopeDefinitionsmethod or PHP 8 Attributes:

protected function scopeDefinitions(): array
{
    return [
        'listing' => ['id', 'name', 'avatar'],
        'detail'  => ['id', 'name', 'email', 'bio', 'created_at'],
    ];
}

public function toArray(Request $request): array
{
    return $this->scoped([
        'id'     => $this->id,
        'name'   => $this->name,
        // ...
    ]);
}

Now, the frontend can just request what it needs via a query parameter: GET /api/users?scope=listing.

Solving the "Nested Resource" Headache

Where this package really shines is how it handles relationships. Usually, when you nest resources, you lose control over what the child resource returns unless you create even more specific classes.

I implemented Scope Cascading to solve this. If you are requesting a PostResource in "listing" mode, you can tell it to automatically put the nested UserResource into "summary" mode.

You can define these mappings in your resource:

protected function scopeMappings(): array
{
    return [
        'listing' => [
            UserResource::class => 'summary',
        ],
    ];
}

This ensures that your API remains efficient and doesn't leak unnecessary data deep within your JSON tree, all while keeping your controller logic clean.

Why This Fits Your Workflow

I made sure that this doesn't break your existing code. If you don't pass a scope, it simply returns everything like a standard Laravel resource. It also plays nicely with whenLoaded(), so your relationship logic stays intact.

Whether you prefer using the scopeDefinitions() method or the new PHP 8 Attributes, the package stays out of your way and handles the heavy lifting of filtering the array keys before the response is sent.

View the package on GitHub: jcfrane/laravel-resource-scope