Skip to content
Muhammet Şafak
tr
Web Development 4 min read

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.

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.

Tags: #Laravel#API
Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

Related Posts

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind