Optimizing API Responses: Conditional Relationship Counts in Laravel Resources

Optimizing API Responses: Conditional Relationship Counts in Laravel Resources

When building APIs with Laravel, it's often necessary to include the count of related models in your responses. However, including these counts can be costly if not done efficiently. Laravel's API Resources provide elegant methods to conditionally include relationship counts, allowing you to optimize your API responses. Let's explore how to leverage these features effectively.

Understanding Conditional Relationship Counts

Laravel allows you to conditionally include relationship counts in your API responses using the whenCounted method. This method only includes the count if it has been explicitly loaded on the model, preventing unnecessary database queries.

Basic Usage of whenCounted

Here's a simple example of how to use whenCounted:

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'posts_count' => $this->whenCounted('posts'),
            'comments_count' => $this->whenCounted('comments'),
        ];
    }
}

In this example, posts_count and comments_count will only be included in the response if they've been loaded using withCount() on the query.

Loading Counts in Controllers

To make use of whenCounted, you need to load the counts in your controller:

public function index()
{
    $users = User::withCount(['posts', 'comments'])->get();
    return UserResource::collection($users);
}

Advanced Usage with Aggregates

Laravel also provides methods for other types of aggregates:

whenAggregated: For general aggregate functions
whenSummed: For sum aggregates
whenAvg: For average aggregates
whenMin: For minimum value aggregates
whenMax: For maximum value aggregates

Here's an example using various aggregates:

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'posts_count' => $this->whenCounted('posts'),
            'comments_count' => $this->whenCounted('comments'),
            'total_post_views' => $this->whenSummed('posts', 'views'),
            'average_post_rating' => $this->whenAvg('posts', 'rating'),
            'latest_post_date' => $this->whenMax('posts', 'created_at'),
        ];
    }
}

Practical Example: Blog API

Let's consider a more comprehensive example for a blog API:

class PostResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->content,
            'author' => new UserResource($this->whenLoaded('author')),
            'comments_count' => $this->whenCounted('comments'),
            'likes_count' => $this->whenCounted('likes'),
            'average_rating' => $this->whenAvg('ratings', 'score'),
            'tags' => TagResource::collection($this->whenLoaded('tags')),
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}

In your controller:

public function index(Request $request)
{
    $posts = Post::query()
        ->with(['author', 'tags'])
        ->withCount(['comments', 'likes'])
        ->withAvg('ratings', 'score');

    if ($request->include_content) {
        $posts->addSelect('content');
    }

    return PostResource::collection($posts->paginate());
}

By leveraging Laravel's conditional relationship counts in API Resources, you can create more efficient and flexible API responses. This approach allows you to include valuable aggregate data in your API without compromising performance, leading to faster and more scalable applications.

If this guide was helpful to you, subscribe to my daily newsletter and give me a follow on X/Twitter. It helps a lot!

Subscribe to Harris Raftopoulos

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe