Laravel & Vue.js - Building a Todo list, Part 3: Using API Resources
Guillaume Briday
4 minutes
Since Laravel version 5.5, we can use API Resources.
Before this addition, we had to use packages like Fractal, which is, I think, the most well-known.
API Resources
allow us to structure the data we will return in JSON format. We are going to transform our data for several reasons:
- We might want to filter the data we send, but also add to it.
- We want to paginate our results by adding pagination information.
- Add nested relationships to our data.
- etc.
For example, I don't need to send the creation or last modification dates of a user. However, I think it's interesting to return calculated information such as the number of tasks or the number of tasks in progress, among others.
Similarly, when retrieving a task, it's convenient to have the author's information directly in the same request rather than having to make a second one. It will be up to us to judge whether it is relevant to nest resources in this way or not.
Structuring our responses will allow us to do this and ensure some consistency in the JSON structures we return.
For the following, I will take examples from the documentation and adapt them to the needs of the project.
Creating a Resource
For the moment, the only resource I return in the application corresponds to users.
$ php artisan make:resource UserResource
A file in the namespace App\Http\Resources
will then be generated.
We will need to modify it to suit our needs:
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
- return parent::toArray($request);
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'email' => $this->email,
+ ];
}
The default method called is named toArray
, and it allows us to access the current request
.
However, the information of the resource we want to transform is directly in the instance of the class accessible via $this
, as usual. We can add as many fields as we want in this array. When I start task management, I could add a tasks_count
key with the value $this->tasks->count
, for example.
Now in our AuthController
, we can directly send our resource in response:
public function me()
{
- return response()->json($this->guard()->user());
+ return new UserResource($this->guard()->user());
}
Previously, all accessible data was returned in JSON format. Now we have only the id, name, and email address of the resource, all within an object called data
:
{
"data": {
"id": 3,
"name": "anakin",
"email": "[email protected]"
}
}
By default, the information is placed in a data
object to allow us to add additional information in a meta
or links
object. We better isolate our data this way.
With this same UserResource
, we can also transform multiple users and even paginated users.
use App\User;
use App\Http\Resources\UserResource;
Route::get('/users', function () {
return UserResource::collection(User::all());
});
We will then have an array of User
formatted as before:
{
"data": [
{
"id": 2,
"name": "luke",
"email": "[email protected]"
},
{
"id": 3,
"name": "anakin",
"email": "[email protected]"
},
# And so on, as many users as there are
]
}
To optimize loading times, we can paginate our queries. If you don't have a lot of data, the difference will be negligible, but there can quickly be a significant difference.
Laravel does all the work for us. Indeed, we just need to use pagination as we usually do and send it to our UserResource
. The result will be a bit different from standard collections.
use App\User;
use App\Http\Resources\UserResource;
Route::get('/users', function () {
return UserResource::collection(User::paginate(1));
});
We then find our formatted information in data
as before, but the links
and meta
objects have been added to the response. They will allow us to have additional information on the URLs to call to get the next pages, the total number of available pages, etc. When retrieving on the client side, this information will be crucial, and having them formatted simplifies the task.
{
"data": [
{
"id": 2,
"name": "luke",
"email": "[email protected]"
}
],
"links": {
"first": "http:\/\/localhost\/api\/v1\/users?page=1",
"last": "http:\/\/localhost\/api\/v1\/users?page=2",
"prev": null,
"next": "http:\/\/localhost\/api\/v1\/users?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 2,
"path": "http:\/\/localhost\/api\/v1\/users",
"per_page": 1,
"to": 1,
"total": 2
}
}
Relationships
We can nest related resources. As I mentioned earlier, I would like to find the user's information directly within my task.
We just need to add a key and call the corresponding resource or resource collection:
For example, here is what a task resource might look like:
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'author' => new User($this->author)
];
}
Note, we are in the namespace App\Http\Resources
, so new User
refers to the resource, not the User
model.
And the result will be:
{
"data": {
"id": 1,
"title": "Travel around the world",
"author": {
"id": 3,
"name": "anakin",
"email": "[email protected]"
}
}
}
We could have sent a collection to have an array of nested objects.
Other methods
There are many other methods available to play with Resources
. In my experience, I've only needed these. I'll let you check the documentation if you need more information; it's really comprehensive.
Conclusion
The changes are already on the GitHub repository.
We can now set up task management!