Laravel API Resources: Fieldsets, Relations &amp; Versioning | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel API Resources: Sparse Fieldsets, Conditional Relationships, and Versioning        On this page       1. [  Beyond toArray: Making API Resources Do Real Work ](#beyond-codetoarraycode-making-api-resources-do-real-work)
2. [  Sparse Fieldsets Without a Package ](#sparse-fieldsets-without-a-package)
3. [  Conditional Relationship Loading ](#conditional-relationship-loading)
4. [  Clean API Versioning Without Route Duplication ](#clean-api-versioning-without-route-duplication)
5. [  Key Takeaways ](#key-takeaways)

  ![Laravel API Resources: Sparse Fieldsets, Conditional Relationships, and Versioning](https://cdn.msaied.com/336/89d518450335e8fcdaa5be882cf4dd3e.png)

  #laravel   #api   #resources   #versioning  

 Laravel API Resources: Sparse Fieldsets, Conditional Relationships, and Versioning 
====================================================================================

     1 Jul 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Beyond toArray: Making API Resources Do Real Work  ](#beyond-codetoarraycode-making-api-resources-do-real-work)
2. [  02   Sparse Fieldsets Without a Package  ](#sparse-fieldsets-without-a-package)
3. [  03   Conditional Relationship Loading  ](#conditional-relationship-loading)
4. [  04   Clean API Versioning Without Route Duplication  ](#clean-api-versioning-without-route-duplication)
5. [  05   Key Takeaways  ](#key-takeaways)

 Beyond `toArray`: Making API Resources Do Real Work
---------------------------------------------------

Laravel's `JsonResource` class is deceptively simple. Most teams use it as a glorified array cast, but it carries enough power to handle sparse fieldsets, conditional relationship loading, and multi-version response contracts — without reaching for a dedicated API layer package.

---

### Sparse Fieldsets Without a Package

JSON:API specifies that clients can request only the fields they need via `?fields[articles]=title,body`. Implementing this in a resource is straightforward once you centralise the parsing.

```php
// app/Http/Resources/Concerns/SparseFieldset.php
trait SparseFieldset
{
    protected function allowed(string $type, array $fields): array
    {
        $requested = request()
            ->input("fields.{$type}");

        if (! $requested) {
            return $fields;
        }

        $keys = array_flip(explode(',', $requested));

        return array_intersect_key($fields, $keys);
    }
}

```

```php
// app/Http/Resources/ArticleResource.php
class ArticleResource extends JsonResource
{
    use SparseFieldset;

    public function toArray(Request $request): array
    {
        return $this->allowed('articles', [
            'id'         => $this->id,
            'title'      => $this->title,
            'body'       => $this->body,
            'created_at' => $this->created_at,
        ]);
    }
}

```

The `allowed()` helper filters the field map to only what the client requested. No extra dependencies, no magic.

---

### Conditional Relationship Loading

The built-in `whenLoaded()` is good, but it only checks whether the relation is already eager-loaded. For APIs that accept an `?include=` parameter, you want to drive eager loading from the resource itself rather than the controller.

```php
// app/Http/Resources/ArticleResource.php
public function with(Request $request): array
{
    return [];
}

public function toArray(Request $request): array
{
    return [
        'id'     => $this->id,
        'title'  => $this->title,
        'author' => $this->whenLoaded('author', fn () =>
            new UserResource($this->author)
        ),
        'tags'   => $this->whenLoaded('tags', fn () =>
            TagResource::collection($this->tags)
        ),
    ];
}

```

In the controller, parse `?include=` once and eager-load accordingly:

```php
// app/Http/Controllers/ArticleController.php
public function show(Article $article): ArticleResource
{
    $includes = array_intersect(
        explode(',', request()->input('include', '')),
        ['author', 'tags'] // allowlist
    );

    return new ArticleResource(
        $article->loadMissing($includes)
    );
}

```

This keeps the allowlist in the controller (where auth context lives) and the shape in the resource.

---

### Clean API Versioning Without Route Duplication

The temptation is to create `V1\ArticleResource` and `V2\ArticleResource` as entirely separate files. That leads to drift. A better pattern: a single resource class that branches on a version header or route prefix.

```php
// app/Http/Resources/ArticleResource.php
public function toArray(Request $request): array
{
    $version = $request->header('X-API-Version', 'v1');

    return match ($version) {
        'v2'    => $this->v2Fields(),
        default => $this->v1Fields(),
    };
}

private function v1Fields(): array
{
    return [
        'id'    => $this->id,
        'title' => $this->title,
    ];
}

private function v2Fields(): array
{
    return [
        'id'       => $this->id,
        'title'    => $this->title,
        'slug'     => $this->slug,
        'metadata' => $this->metadata,
    ];
}

```

When v1 is retired, delete `v1Fields()` and the `match` arm. No orphaned route files, no duplicated middleware stacks.

For larger APIs where versions diverge significantly, namespace the resource classes but share a base:

```php
// app/Http/Resources/V2/ArticleResource.php
class ArticleResource extends \App\Http\Resources\ArticleResource
{
    public function toArray(Request $request): array
    {
        return array_merge(parent::toArray($request), [
            'slug' => $this->slug,
        ]);
    }
}

```

Inheritance is fine here — the relationship is genuinely "is-a".

---

### Key Takeaways

- **Sparse fieldsets** can be implemented with a single trait and `array_intersect_key` — no package needed.
- **Conditional includes** belong in the controller (allowlist) and the resource (`whenLoaded`); never auto-load in the resource.
- **Version branching** inside one resource class is maintainable for minor divergence; namespace inheritance works for major breaks.
- Keep resources focused on *shape*, not business logic — computed values belong in model accessors or dedicated DTOs.
- Always test resources with `ArticleResource::make($model)->toArray(request())` in Pest; it's fast and catches regressions early.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-api-resources-sparse-fieldsets-conditional-relationships-and-versioning&text=Laravel+API+Resources%3A+Sparse+Fieldsets%2C+Conditional+Relationships%2C+and+Versioning) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-api-resources-sparse-fieldsets-conditional-relationships-and-versioning) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Should I implement sparse fieldsets manually or use a JSON:API package like spatie/laravel-json-api-paginate?        For full JSON:API compliance (type objects, links, meta) a package is worth it. For a simpler REST API that just wants field filtering, the trait approach shown here avoids the overhead and keeps your resource shape under your control. 

      Q02  Is it safe to branch on a request header inside a resource's toArray method?        Yes, but inject the version via the constructor if you want the resource to be testable without a real HTTP request. Pass `$version` in and store it as a property — that makes unit testing each version branch trivial. 

      Q03  How do I handle pagination with sparse fieldsets in a collection resource?        The fieldset trait works per-item because each resource instance calls allowed() independently. ResourceCollection wraps the items, so pagination metadata is unaffected. Just ensure your collection class calls the item resource, not a raw array map. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Laravel 13: New Features, Helpers, and Practical Upgrade Notes](https://cdn.msaied.com/339/58c4fa6fe9b6d25a2dac17c621b6f4c6.png) laravel laravel-13 upgrade 

### Laravel 13: New Features, Helpers, and Practical Upgrade Notes

Laravel 13 ships with async-first defaults, a leaner bootstrapping layer, and several quality-of-life helpers....

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-13-new-features-helpers-and-practical-upgrade-notes) [ ![Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping](https://cdn.msaied.com/337/05b39d16d0f88a5fb94d0cf74049b88b.png) laravel laravel-12 upgrade 

### Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping

Laravel 12 ships with a leaner skeleton, first-class route file organisation, and a revised application bootst...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-12-structured-route-files-slim-skeletons-and-the-new-application-bootstrapping) [ ![Laravel WebSocket Broadcasting with Reverb: Scaling Beyond a Single Worker](https://cdn.msaied.com/335/b2461babce9e90ae5fe8dc789cd7046c.png) laravel reverb websockets 

### Laravel WebSocket Broadcasting with Reverb: Scaling Beyond a Single Worker

Reverb ships with a single-process model by default. Learn how to scale it horizontally with Redis pub/sub, tu...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-websocket-broadcasting-with-reverb-scaling-beyond-a-single-worker) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
