Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

Nested API resources

Hello,

For APIs in a Laravel app, I have create several API resources.

I have 3 questions about the resources.

Look at this example where I retrieve all books in the database (no matter about pagination, it's just to explain).

Situation 1 - In some situation I need to return all properties (for example to list the books in a table) and in other situations I need to return only the title (for example in a select/options field). I have created a select() method inside the controller and a BookSelectResource resource.

public function select(Request $request)
{
	$books = Book::all();

	return BookSelectResource::collection($books);
}

I could also do something else : add the properties in the resource with a condition about the route (routeIs('books.select')).

Question 1 - Is there any preference to have for one or the other solution ?

Situation 2 - Some of the resources are nested.

// book resource
return [
	'id' => $this->id,
	'name' => $this->name,
	'categories' => $this->whenLoaded('categories', CategoryResource::collection($this->categories),
];
// category resource
return [
	'id' => $this->id,
	'name' => $this->name,
	'books' => $this->whenLoaded('books', BookResource::collection($this->books),
];

And this generates an infinite loop.

Question 2 - Is there another solution than not using the nested resource and simply return an array like this ?

// book resource
return [
	'id' => $this->id,
	'name' => $this->name,
	'categories' => $this->whenLoaded('categories', $this->categories->each(function ($category) {
		return [
			'id' => $category->id,
			'name' => $category->name,
		];
	})),
];

Situation 3 - A resource returns some specific datas, but if used nested in another resource, I need less datas.

Question 3 - Is it possible to manage this with conditions to add datas in the nested resource ?

Thanks a lot to have read my post and to help me understand what could be the best approach.

V

0 likes
6 replies
LaryAI's avatar
Level 58

Solution to Nested API Resources

Question 1: Preference for Resource Handling

Both approaches you mentioned for handling different data requirements in different contexts are valid. However, using separate resources like BookSelectResource for different representations is generally cleaner and more maintainable. This approach adheres to the Single Responsibility Principle, keeping each resource focused on a specific representation of data.

Using conditions based on routes within a single resource can quickly become complex and hard to manage as your application grows and requirements change. Therefore, I recommend using separate resources for different views of the data.

Question 2: Avoiding Infinite Loops in Nested Resources

The infinite loop occurs because each resource is trying to load the other in a circular manner. To avoid this, you can control the depth of nesting by adding a condition to limit how deep the nesting should go or by using a simpler representation when nesting.

Here's an example of limiting nesting by adding a depth control in your resources:

// BookResource
return [
    'id' => $this->id,
    'name' => $this->name,
    'categories' => $this->whenLoaded('categories', function () {
        return CategoryResource::collection($this->categories)->resolveDepth($this->depth + 1);
    }),
];

// CategoryResource
return [
    'id' => $this->id,
    'name' => $this->name,
    'books' => $this->when($this->depth < 2, function () {
        return BookResource::collection($this->books)->resolveDepth($this->depth + 1);
    }),
];

In this example, resolveDepth is a hypothetical method you would need to implement in your resources to track and limit the depth of nesting.

Question 3: Conditional Data in Nested Resources

Yes, it is possible to manage this with conditions. You can use the request or additional parameters to determine the context in which the resource is being used and adjust the data accordingly.

Here's an example:

// BookResource
return [
    'id' => $this->id,
    'name' => $this->name,
    'categories' => $this->whenLoaded('categories', function () {
        return CategoryResource::collection($this->categories, ['minimal' => true]);
    }),
];

// CategoryResource
public function toArray($request)
{
    $data = [
        'id' => $this->id,
        'name' => $this->name,
    ];

    if (empty($this->additional['minimal'])) {
        $data['books'] = $this->whenLoaded('books', function () {
            return BookResource::collection($this->books);
        });
    }

    return $data;
}

In this example, the CategoryResource checks if a 'minimal' flag is set and adjusts the data it includes accordingly. This allows you to reuse the same resource in different contexts by passing additional parameters.

These solutions should help you manage different data requirements in your API resources effectively and maintainably.

1 like
martinbean's avatar

@vincent15000 The entire point of Eloquent API resources is to standardise how entities are formatted and output in your API. It makes no sense to create a resource class, if you’re then changing what fields are shown when.

1 like
vincent15000's avatar

@martinbean Why is it possible to create a custom resource class if it's not to change what fields are shown ? I don't understand what you mean. Can you explain please ? Or give an example ?

martinbean's avatar
Level 80

@vincent15000 Like I say, the entire point of creating an Eloquent API resource class for a model, is to standardise how that model looks when returning from your API.

If you’re creating a resource class for a book, then that’s so your “book” objects look the same no matter where they’re returned from in your API.

1 like
vincent15000's avatar

@martinbean Ok I think I understand what you mean.

So according to you it's a bad idea to return 2 different resources for a book. My only goal doing that is to send less data to the frontend (no need to get other properties than the id and the title to fill a select/options field). So even for filling a select/options field, you suggest to send the whole data of the books ?

And your answer let me think about something else, I think that I don't use the resource like I should. It's perhaps not a good idea to retrieve (like in my example above) the books in the category resource. Unless I only need this representation of a category. But if I need several different representations of a category, I shouldn't add a book collection inside the category resource and load the additional data (like the books) in another separate resource via another database query. Is it what you seem to suggest me ?

Please or to participate in this conversation.