Back to Blog
Lesson 8 of the Intermediate Laravel: Real-World Application Patterns course
LaravelJune 25, 20263 min read

Resource Controllers and API Responses in Laravel

Learn how to use Laravel API resources to transform model data and return consistent, clean JSON responses for your RESTful applications.

LaravelAPIRESTJSONArchitecturephpbackend

Previously in this course, we covered Mastering REST API Authentication with Laravel Sanctum to secure our endpoints. Now that we have authenticated users, we need to ensure the data we return is clean, consistent, and independent of our database schema.

The Problem with Direct Model Serialization

In early-stage development, it's tempting to return an Eloquent model directly from a controller:

PHP
return Task::all();

While this works, it exposes your database structure to the client. If you rename a database column or add sensitive fields like password_hash to your User model, your API output changes unexpectedly. This creates a tight coupling between your database and your public-facing API.

Laravel’s API resources provide a transformation layer that acts as a buffer. By using these classes, you gain full control over the JSON structure, allowing you to rename keys, hide sensitive data, and include related relationships without altering your underlying models.

Creating and Using API Resources

To standardize our project board, we’ll generate an API resource for our Task model. Run the following command in your terminal:

Bash
php artisan make:resource TaskResource

This creates a file in app/Http/Resources. Open it and define the structure you want to expose:

PHP
namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class TaskResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'status' => $this->status,
            'created_at' => $this->created_at->toDateTimeString(),
            'owner' => new UserResource($this->whenLoaded('user')),
        ];
    }
}

Notice the use of $this->whenLoaded('user'). This is a powerful feature that only includes the user relationship if it has been pre-loaded in the controller, preventing N+1 query issues while maintaining a clean payload.

Integrating with Resource Controllers

Now, update your controller to return the resource. Since we are building a clean, maintainable architecture, we inject our repository (as discussed in Repository Pattern Fundamentals) and wrap the result:

PHP
public function show(int $id)
{
    $task = $this->taskRepository->find($id);

    return new TaskResource($task);
}

For collections, use the collection method:

PHP
public function index()
{
    $tasks = $this->taskRepository->all();

    return TaskResource::collection($tasks);
}

Hands-on Exercise

  1. Generate a UserResource using php artisan make:resource UserResource.
  2. Update the UserResource to return only the id, name, and email fields.
  3. Modify your TaskResource to use UserResource for the owner field.
  4. Test the endpoint in Postman or Insomnia to verify that the JSON output matches your new structure.

Common Pitfalls

  • Forgetting to Eager Load: If you use $this->whenLoaded('relationship') but don't call ->load('relationship') in your controller or repository, the relationship will simply be missing from your JSON. Always check your query logic.
  • Over-Engineering Resources: Don't create a resource for every single edge case. Keep your resources focused on the primary domain entities. If you find yourself needing multiple variations for the same model, consider using conditional attributes or different resource classes.
  • Leaking Internal Data: Always be explicit about what you return in toArray. Never return $this->resource->toArray() directly, as it will dump every column on your model, including internal flags or timestamps you might not want public.

Recap

By using API resources, you decouple your internal database schema from your external contract. This makes your API more resilient to change and provides a cleaner experience for consumers. We've established a pattern that ensures consistent JSON formatting across the entire project board.

Up next: Handling API Validation and Form Requests

Similar Posts