Formatting API Responses with Laravel Resource Classes
How to produce clean API responses with Laravel Resource classes without leaking your database model directly to clients.
One of the traps I fell into most often while building APIs was returning Eloquent models directly as responses. I’d write return $user;, it worked, I moved on. Then a new field got added to the model, or a relationship, or a sensitive column — and the client suddenly received fields it never expected. The API Resource classes introduced in Laravel 5.5 offer a clean solution to this problem.
The Problem: Returning the Model Directly
Consider a User model with fields like email, name, password, remember_token, created_at, and updated_at. If you write this in a controller:
<?php
public function show(User $user)
{
return response()->json($user);
}
password and remember_token will be included in the response. You can prevent this by populating the $hidden array, but that means pushing response-shaping responsibility onto the model itself. As the model grows, keeping track of what to show and what to hide becomes increasingly painful.
There’s another scenario too: you add an is_admin field to the model, it gets sent to the client, and the client starts displaying it to users. Then you rename that field — and the client breaks. When you leak the model directly, the boundary between your database schema and your API contract disappears. These two things need to be kept separate.
Creating an API Resource Class
You can scaffold a resource class quickly with Laravel’s Artisan command:
php artisan make:resource UserResource
This generates app/Http/Resources/UserResource.php. Fill in the toArray method however you like:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at->toDateTimeString(),
];
}
}
Using it in a controller is straightforward:
<?php
public function show(User $user)
{
return new UserResource($user);
}
That’s it. The client now sees exactly what it needs to see — nothing more.
Collection Responses
When you need to return a list instead of a single model, ResourceCollection comes into play. The quickest approach is calling UserResource::collection():
<?php
public function index()
{
return UserResource::collection(User::all());
}
This also works with pagination; when you return paginate(), Laravel automatically appends meta and links blocks.
Conditional Fields
Sometimes I only want to include a field in the response under certain conditions — for example, showing a token only on a newly created record. The when method handles this:
<?php
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'token' => $this->when($this->wasRecentlyCreated, $this->api_token),
];
}
When wasRecentlyCreated returns false, the token key is completely absent from the response — not null, completely gone. That distinction matters.
Related Data
Say you also want to include a user’s roles in the response. You can define another resource class and call it from within:
<?php
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'roles' => RoleResource::collection($this->whenLoaded('roles')),
];
}
The value of whenLoaded is this: if the relationship hasn’t been eager-loaded, it won’t add that field to the response at all. So when you write User::with('roles')->find($id) in the controller, roles appear; when you don’t, they don’t. No N+1 query traps, no unnecessary data fetched per request.
Adding Extra Metadata to the Response
Sometimes you need to wrap the response with additional information: a status code, a message, or some custom metadata beyond pagination. You can override the with method in your resource class for this:
<?php
public function with($request)
{
return [
'meta' => [
'api_version' => '1.0',
],
];
}
The Value of This Layer Becomes Clear as Clients Multiply
When I first used a Resource class, the project had a single client: a web interface. Later, a mobile app was required. Mobile needed different field naming — name instead of full_name, joined_at instead of created_at. I was able to write a separate UserMobileResource for mobile without breaking the web client; both serve the same User model in different shapes. If you’d been returning the model directly, there would be no clean way to make that distinction.
In any project with multiple clients — or even a single client that evolves over time — this transformation layer insulates database changes from the API contract. Adding a new column no longer means breaking the existing response.
Conclusion
API Resource classes initially look like “just another layer to add.” But once I used one on a real project, I never wanted to go back to wiring a model straight to a response. Being able to see and manage exactly which fields get sent out from a single file keeps the client side safe from unexpected changes. Especially when there are multiple consumers — a mobile app, a web app, third-party integrators — the value of this layer becomes all the more apparent.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.