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 note I briefly present the rules that I normally follow when faced with this design task.
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, you may ask. 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. on 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.
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.
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.
PUT) best fits the request purpose, not choosing it may mean throwing away interesting properties, such as caching or fault recovery.
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: