(yes, it doesn’t rhyme however I couldn’t resist the association)
Selecting the proper methods (e.g. GET, POST, PUT, …) to use when designing HTTP based APIS is typically a subject of much debate, and eventually some bike-shedding. In this post I briefly present the rules that I normally follow when faced with this design task.
Don’t go against the HTTP specification
First and foremost, make sure the properties of the chosen methods aren’t violated on the scenario under analysis. The typical offender is using GET for an interaction that requests a state change on the server.
Why is this bad? Because GET is defined to have the safe property, defined as
Request methods are considered “safe” if their defined semantics are essentially read-only; i.e., the client does not request, and does not expect, any state change on the origin server as a result of applying a safe method to a target resource.
Another example is choosing PUT for requests that aren’t idempotent, such as appending an item to a collection.
The idempotent property is defined by RFC 7231 as
A request method is considered “idempotent” if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request.
Breaking these properties is harmful because there may exist system components whose correct behavior depends on them being true. An example is a crawler program that freely follows all GET links in a document, assuming that no state change will be performed by these requests, and that ends up changing the system state.
Another example is an intermediary (e.g. reverse proxy) that automatically retries any failed PUT request (e.g. timeout), assuming they are idempotent. If the PUT is appending items to a collection (append is not idempotent), and the first PUT request was successfully performed and only the response message was lost, then the retry will end up adding two replicated items to the collection.
This violation can also have security implications. For instance, most server frameworks don’t protect GET requests agains CSRF (Cross-Site Request Forgery) attacks because the GET method is not supposed to change state and reads are already protected by the same-origin browser policy.
Take advantage of the method properties
After ensuring the correctness concerns, i.e., ensuring requests don’t violate any property of chosen methods, we can revert our analysis and check if there aren’t any methods that best fit the intended functionality. After having ensured correctness, in this stage our main concern is going to be optimisation.
For instance, if a request defines the complete state for a resource and is idempotent, perhaps a PUT is a better fit than a POST. This is not because a POST will produce incorrect behavior but because using a PUT may induce better system properties. For instance, an intermediary (e.g. reverse proxy or framework middleware) may automatically retry failed requests, and by this provide some fault recovery.
When nothing else fits, use POST
Contrary to some HTTP myths, the POST is not solely intended to create resources. In fact, the newer RFC 7231 states
The POST method requests that the target resource process the representation enclosed in the request according to the resource’s own specific semantics
The “according to the resource’s own specific semantics” effectively allows us to use POST for requests with any semantics. However the fact that it allows us doesn’t mean that we always should. Again, if another method (e.g. GET or PUT) best fits the request purpose, not choosing it may mean throwing away interesting properties, such as caching or fault recovery.
Does my API look RESTful in this method?
One thing that I always avoid is deciding based on the apparent “RESTfullness” of the method – For instance, an API doesn’t have to use PUT to be RESTful.
First and foremost we should think in terms of system properties and use HTTP accordingly. That implies:
- Not violating its rules – E.g. what can go wrong if I choose PUT for this request?
- Taking advantage of its benefits – E.g. what do I loose if I don’t choose PUT for this request?
I hope this helps. Feedback welcomed.