Laravel is a great framework that is popular among developers and is used for all kinds of projects. One of Laravel's usages is creating APIs.
One asset that you can use to make sure your APIs in Laravel are uniform across requests and the correct desired data is being returned in all APIs is Laravel Resources.
In this tutorial, we'll learn what Laravel Resources are, and how to use them in our Laravel project. You can find the code for this tutorial in this GitHub Repository.
What are Laravel Resources
Laravel Resources are another layer added on top of Laravel Eloquent models. It allows creating a uniform format or shapes for the data returned for these Eloquent models.
Why Use Laravel Resources
It's important to make sure the correct data is being sent in your APIs. For example, you don't want to accidentally send secret data like keys or tokens related to users or other models in your APIs.
Furthermore, when building APIs, it's important that the structure of data is the same across requests. This also helps other developers that might be using your API, as it ensures the data structure across different requests.
Prerequisites
Before we see how we can use Resources in Laravel, we'll first undergo a few commands to set up our Laravel project. If you already have your project set up you can skip this step.
Create Laravel Project
In your terminal, run the following command to create a Laravel project with Composer:
composer create-project laravel/laravel laravel-resources
After the command is done executing, change to the newly created project directory:
cd laravel-resources
Prepare the Database
Next, we'll set up the database. You'll need to make sure you have a MySQL server installed if that's your choice of database.
Setup Database and Connection
We'll create a new database laravel-resources
. You can create a database of any name you want.
After you create the database, go to .env
and change the configurations related to the database connection:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-resources
DB_USERNAME=root
DB_PASSWORD=
Make sure to place your database name in DB_DATABASE
, database username in DB_USERNAME
, and database password in DB_PASSWORD
.
Changes to Default User Model
In this tutorial, we'll make use of the default User model that comes with every Laravel project. However, we'll make a slight change by adding a new field age
.
First, we'll make changes to the migration file. If you're not sure what a migration is, database migrations in Laravel allow defining the database schema easily.
Open database/migrations/2014_10_12_000000_create_users_table.php
and add the following line inside Schema::create
callback function:
$table->integer('age');
This will add a new age
field in the users
table.
Next, we'll modify the user factory as we'll use it to create random users. If you're not sure what a factory is, Laravel provides a way to create database records for a model for testing.
Open database/factories/UserFactory.php
and inside the definition
function add the following at the end of the returned array:
'age' => $this->faker->numberBetween(10, 90)
So, the definition
function should look like this:
public function definition()
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
'age' => $this->faker->numberBetween(10, 90)
];
}
Finally, we'll make a change to the User model. Open app/Models/User.php
and add the age
field to the fillable
array:
protected $fillable = [
'name',
'email',
'password',
'age'
];
Create Database Tables
We'll now create the database tables using Laravel's migrations. Run the following command:
php artisan migrate
This will create the users
table as well as other tables that come by default in Laravel.
Add Fake Users
Next, we'll utilize the UserFactory to create fake users in our database for testing. Go to database/seeders/DatabaseSeeder.php
and uncomment the following line:
// \App\Models\User::factory(10)->create();
Then, run the following command:
php artisan db:seed
And with that, we'll have 10 fake users in our database.
Create a User Resource
Resources are created on top of an eloquent model. This means that typically you'd need a resource for every eloquent model separately.
We'll create a resource for our User model, then see how we can use it in requests.
To create a resource, run the following command:
php artisan make:resource UserResource
This will create a new class UserResource
in app/Http/Resources
. This is where all Resources that you will create will reside in.
If you open UserResource
now, you'll see that there's already a toArray
method. This is the method that is used to return the data related to the user. By default, it just returns the result of toArray
method on the user.
The current model that the resource is being used for, in this case, it's a user model, is represented inside the resource class by the field resource
in the class. So, you can reference the underlying class with $this->resource
.
We'll change the returned array of toArray
in UserResource
to only return the user's id, name, and email. Change the content of toArray
to the following:
return [
'id' => $this->resource->id,
'name' => $this->resource->name,
'email' => $this->resource->email
];
Remember, $this->resource
refers to an instance of the User model.
We've created our first resource! We'll see it in action in the next section.
Create API Endpoint
In this section, we'll create an API endpoint that allows us to retrieve the data of any user using their ID.
First, we'll create a new controller UserController
to handle API requests related to the User
model. Run the following command in your terminal:
php artisan make:controller UserController
Then, go to app/Http/Controllers/UserController.php
. We'll create a new method getUser
that accepts an ID and should return the user's data. Add the following inside the UserController
class:
public function getUser($id) {
$user = User::query()->find($id);
if (!$user) {
return response()->json(['success' => false, 'message' => 'User does not exist']);
}
return response()->json(['success' => true, 'user' => new UserResource($user)]);
}
We first check that a user with that ID exists. If the user doesn't exist we return a JSON response withsuccess
set to false
with a message indicating that the user does not exist.
If the user exists, we return a JSON response with success
set to true
and user
set to an instance of UserResource
.
Note that to create an instance of a resource, you need to pass it the model the resource should use as a parameter to the constructor like so:
new UserResource($user)
That's all we need to do! We just need to add the new API endpoint as a route.
Open routes/api.php
and add the following:
Route::get('/user/{id}', [UserController::class, 'getUser']);
Our API is ready! Time to test it.
Start your server with the following command:
php artisan serve
Then, to test the API endpoint you can use something like Postman or Thunder Client. As this is a GET request without authentication you can also just open the URLs in your browser.
Send a GET request to localhost:8000/api/user/1
or open the URL in your browser. You'll see that the endpoint will return a JSON response with 2 keys, success
and user
. Notice how the user
object includes just the user's id
, name
, and email
which is what we added in our UserResource.
You can try changing the id
, which is 1
in the above request, to other IDs and see how it returns the same response structure for all users.
Create a User Collection Resource
Resources are not just for single model instances. You can create a collection resource that takes a collection of model instances as a parameter and formats the collection as necessary.
To create a collection resource for the User model run the following command:
php artisan make:resource UserCollection --collection
This will create the collection resource class at app/Http/Resources/UserCollection.php
.
Note that for collection resources there is a naming convention to make sure that the collection is associated with the correct resource model for the instance. The naming convention is <Model>Collection
.
So, since our resource name is model name is User
the collection resource name will be UserCollection
.
If you wish to name the collection differently, you can specify the resource class using the collects
field on the collection class:
public $collects = User::class;
Collection resources are similar to singular resources. There is a toArray
method that we will use to define the structure of collections.
Change the content of toArray
method to the following:
$count = $this->collection->count();
return [
'count' => $count,
'data' => $this->collection
];
Note that $this->collection
will return an array of objects with the structure the same as defined in UserResource
for each user instance in the collection.
Create API Endpoint
We'll now create an API endpoint that will allow us to get all users. In UserController
that we created previously add the following method:
public function getUsers () {
$users = User::query()->get();
return response()->json(['success' => true, 'users' => new UserCollection($users)]);
}
To create an instance of the collection resource, similar to the singular resource, you need to pass the collection of models as a parameter to the constructor.
Then, add the new route in routes/api.php
:
Route::get('/user', [UserController::class, 'getUsers']);
Let's test it. Send a GET request to localhost:8000/api/user
or open it in your browser. You'll see that it returns 2 keys success
and users
. users
will be an array of users and all having the same structure as that defined in UserResource
.
This shows an example of how defining a Resource guarantees the same structure across requests.
Conditional Fields
Sometimes you might need to add a field based on a condition. To do so, you can use the when
method defined on resources.
We'll add an old
field that should appear only when the user's age is greater than 18
. Add the following field in the returned array in toArray
in UserResource
:
'old' => $this->when($this->resource->age > 18, true)
This will add the old
field only if the user's age is greater than 18. Note that the first paramter of when
is the condition and the second parameter is the value expected.
Try sending a GET request to localhost:8000/api/user
or open the URL in your browser now. You'll see that some users will have the old
field while others don't.
Note that the result for your request will defer as the users generated by the factory are random.
Conclusion
Laravel's API Resources allow making your returned data in APIs structured and uniform. It also guarantees what the fields returned will be so no fields will be accidentally exposed.
You can check out more details about Laravel Resources in their documentation.