Eloquent: API Resources
Tags: laravel, eloquent, api, php
Published: 2022-08-19 09:11:48

**What is API Resource in Laravel?**
The best description comes from [the official documentation](https://laravel.com/docs/9.x/eloquent-resources):

“it’s a (transformation) layer that sits between your Eloquent models and the JSON responses that are actually returned to your application’s users.”

In other words, it is the class responsible for formatting the data that should be returned to the user as a response to a request.
Instead of getting an instance of a model and manually set which field should be returned, you may use this layer.

Removing this responsibility from your controller, model or other class, helps you to keep your code clean and easy to maintain.


**Let’s see how it works**
We’ll create some data just for testing purpose

Since new Laravel projects comes with a default User model and migrations, let’s use this one. Let’s set the database connection and create some fake data.

So create a new database, open your .env file and set the proper configuration there. My database config looks like this:

```
#.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=api_resources
DB_USERNAME=root
DB_PASSWORD=
```

Now let’s create some fake data for testing. In my DatabaseSeeder I just uncommented the user factory line:

```
#database\seeders\DatabaseSeeder.php

<?php

namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        \App\Models\User::factory(10)->create();
    }
}
```

And then I ran the migrate using the seed flag:
`php artisan migrate --seed`

If you’re following these steps, I hope you have something like this 🤞:
![migrations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swcwpj75saz8pwhutz23.png)

Now I have the user’s table created and populated with these ten users we defined in the factory.

---

**Creating and testing the endpoint**

Now I just opened my api routes file, removed everything there and created a get route that returns the first instance of the User model.

```
#routes\api.php

<?php

use App\Models\User;
use Illuminate\Support\Facades\Route;

Route::get('/user', fn() => User::first() );

/* Sure in real life the path would be the resource name in plural and you wont put this stuff in your routes file, I’m just trying to do the very basic here */
```
So I ran the `php artisan server` and hit the server:

![server running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmen2viq1vo6qic0t4m4.png)

This is what I received when I made a GET to 127.0.0.1:8000/api/user . It is my first instance of the User model.

You should receive something different since the factory creates User entries randomly.

![Get user model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idjchi07efjfxaq1ytk9.png)

It works. Fine! My route returned the id, name, email, email_verified_at and timestamps fields. Those are the default User attributes.

---

**What if we wanted to return only specific fields?**

What should I do to return just the user `name` and `email` and 'hide' the other attributes?

By default a model instance return all of it’s attributes, so if you need to show only specific ones you have a lot of ways to do it. One of them is setting what you want directly on the instance.

Something like:

```
#routes\api.php

<?php

use App\Models\User;
use Illuminate\Support\Facades\Route;

Route::get('/user', fn() => User::get(['name', 'email'])->first() );
```

Now it should return something like:

![get user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fus49vbe8o695x4zsac5.png)


**Looks good, right? But…**

What if the model had lot more attributes? Or what if I wanted to display some of user relations? What if depending on the user role, some field could be displayed, and some others couldn’t?

There are a lot more “what if” that can make your code turn into a mess if you use this approach.

So instead of setting each attribute directly in the route, controller or where else, you may use the Eloquent Api Resource.

---

**Creating an Api Resource**

Assuming you’ve read [the documentation](https://laravel.com/docs/9.x/eloquent-resources), let’s create our transformation layer by running the following command:
`php artisan make:resource UserResource`

It will create a class called “UserResource” in this path app\Http\Resources. It should be something like this:

![Api resource](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t37km1fxli8da3istlbm.png)

Since we have a layer to set the fields that will be returned, we don’t need to do this in the route, so let’s get back to the route file and undo that get(['name', 'email']) and pass the instance of the user to this new layer (UserResource):

![routes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/30kch7jwwm0a7anpe5hi.png)

And finally let’s set the attributes we want to show in the UserResource

![userResource](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z4q1remfx2q2az5gtqoj.png)

Notice that the changes in the UserResource file were:

**Moving from:**

```
return parent::toArray($request);
```

**To:**

```
return [ 
  'name' => $this->name,
  'email' => $this->email,
];
```

**$this** represents the instance of the model retrieved and name and email are attributes of this model

And the result is:

![Return](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/habrsgsazzex1fgghy7k.png)

---

![lazy dog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96gwzamz616j8kdr443z.jpeg)

I know, I know… it looks the same, but imagine the possibilities whenever you want to get a lot of different attributes, including attributes from related models.

It was a very simple example, but trust me: it helps a lot when your project grows up and you want to keep the principle of the single responsibility.

Your code will be cleaner and easy to understand.
The next developer to get your code (and the future you) will be thank to your choice.


![thanks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9beija9divlnwlk9visu.gif)