ttp verbs, or header fields, are an important part of a RESTful api request, they allow us to separate and identify the requests purpose and enable all possible CRUD (create, read/retrieve, update, delete) operations which end users/BAs etc are more commonly used to describing.
There is not a direct mapping to between the http verbs and the CRUD operations and a misunderstanding of this mapping can be the source of over complicated explanations and wrong uses. This post looks to explain how this misunderstanding can come about and set right the purpose of the verbs.The term ‘Right Use’ this should be caveated, there really is no right and wrong way of using this technology (as you will find at the end of this post), if it serves its purpose – however there are better rights than others!
The most commonly used set of http verbs include:
Most of these are obvious and there is an clear relationship with two of the CRUD operations Delete (DELETE) and Read (GET). But what about the others? Create and Update do not really lend themselves nicely to POST, PUT or PATCH. Sometimes PUT is used for Update and POST for Create, the assumption being that it is best to use POST for updates. This is wrong and misinterprets the purpose of these headers, especially the relationships of PUT and POST as Create and Update. PATCH is often left out altogether as superfluous.
Creating New Items
To understand what is going on it is best to leave aside the concept of Create and Update as determined by CRUD and instead consider the concept of addressing with in an API framework. When calling a resource we either know what its specific URI (Uniform Resource Identifier) is/should be or we don’t.
Consider the following two examples from an ecommerce type application:
In the first we know the exact address of the product, in the second we do not and probably do not even want to. When creating and updating the chair product using CRUD we would need two separate addresses CREATE /system/products/furniture/officeChair and UPDATE /system/products/furniture/officeChair. But with our http verbs we could do both with PUT /system/products/furniture/officeChair. This works because we know the location of the item up front.
If we wanted the system to allocate the Id we can just call the group location and use the POST verb i.e. POST /system/products/furniture/ the system would then respond with the id maybe 12345 and if the product needed amending/deleting then PUT/DELETE /system/products/furniture/12345 can be used. It is easy to see how this can be applied to items like system messages were we do not really care about the Id. POST /system/comms/message would result in a system generated Id, GET POST /system/comms/message would read a collection of messages and DELETE/system/comms/message/123 remove it after reading. PUT of course would not be applicable because we do not know, want to or need to know or control the id of the message. It is a message not a product.
Determining use of the verb by address rather than purpose can seem odd if we approach the topic with from a CRUD perspective rather than an addressing position but it makes a lot of sense and simplifies the situation. No more confusion about the purpose of the end point, instead we are just interaction with the data. This table gives an overview.
|PUT||Create(update)||single||create(update) an item at a known address|
|POST||Create||both||create an item to be allocated an id/address by the system|
|GET||Read||both||read on or more items|
|DELETE||Delete||both||delete one or more items|
|PATCH||Update||both||partially update one or more items|
PUT is used to interact with an item in a know location, POST allows us to create an item and have the system determine its id and hence its location.
What about PATCH?
When creating with PUT or POST there would need to be a minimum set of properties needed to create an item. Imagine though if we had a group of items we wanted to update maybe we wanted to apply a discount of 10% to all furniture items. This would mean we needed to cycle through each item, read it and then update it with all the required fields plus the updated discount rate. Or worse, we would remove the validation in order to update just the single value on the collection.
PATCH allows us to partially update an item. If we do not apply the mandatory field validation to the PATCH* we do not need to read and resave when running mass updates. We just send the update to the group and update the field for all items. We can be confident the items have the correct fields because the validation still applies to the PUT and POST when they were created and we can use the header to determine whether to apply the validation or not.
* In reality the validation will still be there but will only be applied to properties that are actually sent. This would not work with POST/PUT as it would enable items to be partially created.
As I mentioned earlier sometimes there is not really a right and a wrong way to approach this topic but certainly some right ways are better than others! Traditionally GET is used to read and should not really include a body (it is possible so send a body but it may not be supported, Fiddler for example does not). As more an more complex query mechanisms such as oData and GraphQl come online and are improved, sending sophisticated requests may require body data as well as query string mechanisms and this may mean creates/updates/deletions etc all run through a single verb like PUT which does accept a body. Or GET may be used for creates and updates. This aspect really then religates the http mechanism to a transport level rather than a functional which is probably inevitable given the more advanced sophistication available in systems like oData, GraphQL and ORDS. There are suggestions that the increased load associated with these services will limit them, but bandwidth is cheap and the advanced features offered far outweigh any increase in traffic.