I have a scenario where I'm designing a RESTful API endpoint for managing items in a shopping cart. The endpoint is defined as follows:
PUT /carts/{{cartId}}/items
The expected JSON body for the request is:
{
"sku": 100001,
"quantity": 1,
"price": 49.95
}
The current implementation on the server-side has a behavior that I find useful for reducing load. If the item already exists in the cart, it increments the quantity by 1; otherwise, it adds a new item to the cart with the provided details.
Now, my concern is whether this aligns with the PUT method's definition in the RESTful principles. My understanding is that PUT should replace the entire resource or create a new one if it doesn't exist.
Here are my specific questions:
To begin, a reminder: the semantics of PUT are currently defined by RFC 9110. They are, in essence: "please make your current representation for this resource look like the enclosed representation."
This is the HTTP method we would use if we wanted to, for instance, fix a spelling error in a web page. A general purpose editor would fetch a copy of the HTML (via GET /example), make changes to the local copy, and then send a copy of the edited web page back to the server (via PUT /example).
REST's uniform interface constraint says that everyone should understand messages the same way.
Therefore
PUT /carts/{{cartId}}/items
should mean exactly the same thing as
PUT /example
Does the current implementation violate the PUT definition, given that it doesn't replace the entire resource but rather modifies it in a specific way (adding quantity +1)?
So there are two important ideas to distinguish.
Trying to create a specialized meaning for PUT messages to your resources is just wrong (by the uniform interface constraint).
BUT....
The uniform interface constraint applies to request/response semantics, not to the implementation of the resource.
HTTP does not attempt to require the results of a GET to be safe. What it does is require that the semantics of the operation be safe, and therefore it is a fault of the implementation, not the interface or the user of that interface, if anything happens as a result that causes loss of property -- Fielding, 2002
By the same reasoning, you can do whatever you want in the implementation of your PUT handler... but if something expensive results from the discrepancy between what you actually did and what the message asked, the lawyers are going to conclude that your implementation is at fault, rather than the fault of the client who assumed that you understood what PUT means.
If the current implementation doesn't align with the PUT method's definition, what would be a more appropriate HTTP method for this scenario?
POST.
Are there any best practices or alternative approaches for handling such scenarios
Primarily this: your API is a facade that acts-like-a general purpose document store. Your resource model is the collection of affordances you provide so that general purpose HTTP messages can be used to send information to you, or request information from you.
The goal isn't to create specialized messages that trigger useful work; instead, the goal is to design a resource model that, in response to general purpose messages, performs useful work.
The way Jim Webber describes it: "specialization and innovation depend on an open set". For SOAP, we have an extensible set of messages being sent to a fixed set of endpoints. If you want to do something new, you invent a new message. REST takes the other approach - the set of messages is small, but we can innovate as much as we want in the set of resources.