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

Kisiara's avatar
Level 15

What is the recommended approach for designing nested API responses

I have a set of models that have a one on one relationship as defined below. Each of the models has a name attribute

  • Product category
  • Product belongs to Product category
  • Brand belongs to Product
  • Package belongs to Brand
  • Metric belongs to Package

By this definition, its will mean that any granchild will belong to a (one) grandparent. e.g metric will belong to brand.

When designing the response for a child (e.g metric), what is the best way to return this data and why;

  • Option 1 [nested]
[
    "name" => "Kilograms",
    "package" => [
        "name" => "Blue band low fat",
        "brand" => [
            "name" => '"Blue band",
            "product" => [
                "name" => "Margarine",
                "product_category" => [
                    "name" => "Spread"
                ]
            ]
        ]
    ] 		
]
  • Option 2 [flat]
[
    "name" => "Kilograms",
    "package" => [
        "name" => "Blue band low fat"
    ],
    "brand" => [
        "name" => '"Blue band"
    ],
    "product" => [
        "name" => "Margarine"
    ],
    "product_category" => [
        "name" => "Spread"
    ]
]

Bonus Question: For option 2 (flat), how do you achieve it using Laravel's API Resources

0 likes
6 replies
piljac1's avatar

I think the first one is more common and makes more sense to me because you can clearly understand the relations. However, I'm really confused by the fact that a Brand belongs to a Product (because a brand should have many products). It should be the other way around. Same thing for the relation between Brand and Package. Because normally a Package (or plan) is linked to a product and not a brand., but idk about your business logic, so I might be making wrong assumptions here.

1 like
Kisiara's avatar
Level 15

@piljac1

Thanks so much for the response. I agree that the first one certainly does make more sense from a relational perspective and support for it comes off the box through model relationships. I really don't have any functional reason for considering the second one (flat) other than it is easier to read and my head keeps screaming "readability counts"

As for the structure, its true that its all based on the business definitions of the client, but I have not committed too much yet on the naming and I can change to be as incorporating to the global definitions as I can be.

Suppose you have want to sell 500ml of diet coke, how would you identify the following; I have included my reasoning in brackets, I hope it doesn't bias your answer.

Soft drink (product category) Soda (product) Coke (brand) Diet coke (package) 500ml (metric)

piljac1's avatar

@Kisiara I'm a bit confused. If an example of a metric name is 500ml, how would a metric resource (in your original post), know what package/product/brand/etc. to return ? Since you could have water bottles, juices, and many more products that could be 500ml.

Kisiara's avatar
Level 15

@piljac1

The metric has a package_id. Note in my original post that the metric belong to a package. So it’ll be

500ml -> Coke Zero

In the metrics table, 500ml is not unique. (This was a business decision that provided more pros). Therefore, each entry will have its associated package.

I know this can span a conversation around normalization but that’s another discussion all together.

piljac1's avatar

@Kisiara I "get it", but I don't haha. I mean that I get your relations, but I don't believe they're logical in an actually normalized relational database. But as you stated, that's another discussion. That being said, I don't know which option (1 or 2) is the best anymore, because the relations are not really logical in my opinion. As for your bonus questions, you could easily achieve it by chaining your relations and pass the value to its related resource constructor (I would probably add a ternary or leverage when to avoid passing null to the constructor if the relation value does not exist or couldn't be retrieved).

Ex. :

return [
    'name' => 'Kilograms',
    'package' => $this->when($this->package, new PackageResource($this->package)),
    'brand' => $this->when($this->package?->brand, new BrandResource($this->package->brand)),
    // etc.
];
Kisiara's avatar
Level 15

@piljac1 I honestly appreciate the feedback. Wish there was a way to discuss about this further by sharing ideas as well as outline some of the challenges that I have actually encountered whilst building this. Honeslty, I dont beleive its the best approach at it myself but I dont have enough information to go by (Inventory structure is quite complex) and I am currently trying to launch the product and hopefully gather more information from usage and customer behavior.

For now, I think I will go with option 1 and see how that fairs. If there are compelling reasons to change, I will do what every engineer does and complain about technical debt ... hehe ... JK, I will refactor it to fit the use case.

Nonetheless, I absolutely appreciate the feedback and Im grateful for the time you took to respond.

Please or to participate in this conversation.