Reputation: 2440
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:
Order
with lots of OrderItems
Order
's properties, like IsVoided
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:
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 } ] }
Order
's properties, like IsVoided
(but NOT items): [PATCH] /api/orders/{orderId}
[ { "op": "replace", "path": "/IsVoided", "value": true } ]
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:
[POST] /api/orders/{orderId}/items
, with payload: { "quantity": 25, "price": 50 }
[PUT] /api/orders/{orderId}/items/{itemId}
, with payload: { "quantity": 25, "price": 50 }
[DELETE] /api/orders/{orderId}/items/{itemId}
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
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