Ish Thomas
Ish Thomas

Reputation: 2440

How to update a collection in Web API?

I have a basic Order-OrderItem situation and I'm struggling how to update Items in one request (or if should I?) Let me explain and I'm gonna ask the question at the end.

Models:

public class Order
{
    public int OrderId { get; set; }
    public string CustomerId { get; set; }
    public bool IsVoided { get; set; }

    public List<OrderItem> Items { get; set; }
}

public class OrderItem
{
    public int OrderItemId { get; set; }
    public string Name { get; set; }
    public int Quantity { get; set; }
    public int Price { get; set; }
}

Use cases I want to cover:

  1. Add Order with lots of OrderItems
  2. Update Order's properties, like IsVoided
  3. Update Order's items (multiple items at once). Meaning - user will change multiple items in the UI and the request will be sent when pressed "Save". That includes, updating the current items, but also adding new items or removing items as well. Partial success shouldn't be allowed.

API's URIs to cover each use case:

  1. Add Order with lots of OrderItems: [POST] /api/orders/ with payload:
{
    "customerId": 805,
    "isVoided": "false",
    "items": [
        {
            "itemId": 112233,
            "quantity": 25,
            "price": 50
        },
        {
            "itemId": 445566,
            "quantity": 20,
            "price": 40
        }
    ]
}
  1. Update Order's properties, like IsVoided (but NOT items): [PATCH] /api/orders/{orderId}
[
  {
      "op": "replace",
      "path": "/IsVoided",
      "value": true
  }
]
  1. Update Order's items (multiple items at once)

Here's the problem I'm having... I have a few ideas:

Solution A: Update Order's items one by one, so endpoints:

Pros: Clean architecture. You Add/Update/Delete the actually Entity, using Entity's endpoint.

Cons: If user updates 500 items and click "Save", it will result in 500 requests to the server. Also, it will accept partial success

Solution B: Update Order's items at once by updating the Order: [PUT] /api/orders/{orderId} with payload:

{
    "customerId": 805,
    "isVoided": "false",
    "items": [
        {
            "itemId": 112233,
            "quantity": 25,
            "price": 50
        },
        {
            "itemId": 445566,
            "quantity": 20,
            "price": 40
        }
    ]
}

Pros: Performance, Partial success will not be allowed.

Cons: If user updates 50 items, deletes 50 and adds new 50 items to the order, in one request (a PUT request on the Order entitiy) we will esentially add, update, remove 50 items on a different entity - OrderItem. I'm concerned if this is the good RESTful practice.

Solution C: Update Order's items at once by updating... the collection: [PUT] /api/orders/{orderId}/items with payload:

[
    {
        "itemId": 112233,
        "quantity": 25,
        "price": 50
    },
    {
        "itemId": 445566,
        "quantity": 20,
        "price": 40
    }
]

The collection in the payload, will completely replace the collection in the system, including Add and Remove operations.

Pros: Performance, Partial success will not be allowed, You don't mess with the parent Entity.

Cons: Is this a good practice to call PUT request on a collection. Ususally when you have PUT, the URI ends with some kinda ID (you're updating an entitiy). In this case, the URI will end with "items". Is this how it's done?

Solution D: A different solution, possibly with PATCH? Never done that before, but maybe it's possible to send PATCH for an Order entity, patching the collection of Items. In JsonDocument's value, I would pass the collection of new items, items to remove and updated items?

So, my question is: Which of these solution is the best for this situation? A, B, C or (if exists)D? Or other solution I didn't think about?

Upvotes: 4

Views: 2514

Answers (1)

Athanasios Kataras
Athanasios Kataras

Reputation: 26342

Solution A is totally fine if you don't have many items. It does not suit your case as it makes your api chatty if you have many requests.

Solution B sends a lot at once. It adheres to the practice of updating with the full resource object and there is even an http status code to indicate partial success. Response object is a consideration to let the consumer know the new urls of successful and indicate the ones that failed if any and why. Go with it or D.

Solution C is not that restful. You are not really updating any single resource and it will be hard to understand for the consumers.

Solution D is a merge of B and C. I would expect it to be used here as you are not really updating the full object. You can use the same url as B

Several applications extending the Hypertext Transfer Protocol (HTTP) require a feature to do partial resource modification. The existing HTTP PUT method only allows a complete replacement of a document. This proposal adds a new HTTP method, PATCH, to modify an existing HTTP resource. – RFC 5789

Upvotes: 1

Related Questions