C# REST API Design – Dealing with Child Collections Updates in Web API

api-designasp.net-corecrestweb-api

Let's take some classic enterprise example: Order and OrderItem

public class Order
{
    public Guid Id { get; set; }
    public ICollection<OrderItem> Items { get; set; }
}

public class OrderItem
{
    public Guid Id { get; set; }
    //...
}

Now, what's the most reasonable way to update the order's items using PUT or PATCH (JSON Patch) ?

Should I PUT api/orders/{id} with a new collection (with removed, added or updated items) or handle it differently someway? Like instead of returning a serialized collection of items, return list or links for each item like: api/orders/{id}/items/{id}. So it would be more HATEOAS like and REST like…

public class Order
{
    public Guid Id { get; set; }
    public ICollection<Link> Items { get; set; }
}

Then if I wanted to update or remove some item from the order I would use those links, but the downside of this is that client-side implementation will not be that straightforward.

The other solution I can think of is returning ids of items like:

public class Order
{
    public Guid Id { get; set; }
    public ICollection<Guid> Items { get; set; }
}

but it doesn't look too nice for me, so what is more or less industry standard for dealing with this?

  • Returning embedded items
  • Returning item URLs
  • Returning item ids

Best Answer

If Order has the OrderItems attached then you should just update Order and not provide api methods for OrderItems.

so

public class Order
{
    public Guid Id { get; set; }
    public ICollection<OrderItem> Items { get; set; }
}

api/orders/UpdateOrder {"Id" : "guid" "Items" : [{...},{...}] }
api/orders/GetOrderById

If OrderItem just has an OrderId and Order doesn't contain a list of them, then you should have api methods for OrderItems

so.

public class Order
{
    public Guid Id { get; set; }
}

public class OrderItem
{
    public Guid Id { get; set; }
    public Guid OrderId { get; set; }
}

api/orders/UpdateOrder {"Id" : "guid"}
api/orders/GetOrderById

api/orders/UpdateOrderItem {...}
api/orders/GetOrderItemsByOrderId
api/orders/DeleteOrderItem

*avoiding HTTP verbs for clarity

At first glance the complex object/aggregate/first approach seems far superior. But as you object starts getting larger, and you are only needing some of the child objects for certain operations, the second approach can become more efficient.

For example, you can imagine that I might want to know and do lots of things about/with a Company object without loading the List of all its Employees past and present every time.