
What is API Resource in Laravel?
The best description comes fromthe official documentation:
“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:
#.envDB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=api_resourcesDB_USERNAME=rootDB_PASSWORD=
Now let’s create some fake data for testing. In my DatabaseSeeder I just uncommented the user factory line:
#database\seeders\DatabaseSeeder.php<?phpnamespace 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 🤞:
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<?phpuse 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 thephp artisan server
and hit the server:
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.
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 username
andemail
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<?phpuse App\Models\User;use Illuminate\Support\Facades\Route;Route::get('/user', fn() => User::get(['name', 'email'])->first() );
Now it should return something like:
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 readthe documentation, 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:
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):
And finally let’s set the attributes we want to show in the UserResource
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:
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.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse