Expandable relationships with sparse expansions
Avoid over-fetching by default while letting clients pull related data in one call when needed using expand parameters.
Instead of always embedding everything, or always forcing N+1 client calls, you expose a base representation and allow clients to “expand” specific relationships via a query parameter like expand=.
Example: base order representation.
GET /orders/ord_123Accept: application/jsonHTTP/1.1 200 OKContent-Type: application/json
{ "id": "ord_123", "total": 4900, "currency": "GBP", "customer_id": "cus_123", "item_ids": ["item_1", "item_2"]}Expanded view:
GET /orders/ord_123?expand=customer,itemsAccept: application/json
HTTP/1.1 200 OKContent-Type: application/json
{ "id": "ord_123", "total": 4900, "currency": "GBP", "customer": { "id": "cus_123", "name": "Ada Lovelace", "email": "ada@example.test" }, "items": [ { "id": "item_1", "sku": "TSHIRT-001", "quantity": 1 }, { "id": "item_2", "sku": "MUG-042", "quantity": 2 } ]}You can also support nested expansions such as expand=customer.address.
Trade-offs and notes 
Pros
-
Default responses stay lean and fast.
-
Clients can trade extra payload for fewer round trips when it makes sense.
-
Reduces proliferation of “special” endpoints like
/orders-with-customer-and-items.
Cons
-
Implementation complexity grows with the depth of expansions.
-
Harder to cache responses if variations explode.
DX tips
-
Document available expansion paths per resource (
customer, items, items.product, and so on). -
Enforce a sane limit on the number of expansions to avoid “fetch everything” abuse.
-
Consider combining with sparse fieldsets (
fields[customer]=id,name).