Imagine an HTTP REST endpoint, where a resource is inserted, and that the resource is understood to be a "message". Each individual message is identified by a unique identifier, such as a GUID value of some sort. The same message cannot be duplicated.
Now, in a lot of cases, that would lend itself to the PUT
verb, since it's idempotent. However consider the following scenario:
1. The sender sends this message to the receiver:
{
"id": 123,
"text": "original text"
}
2. Now the receiver has this value for the message stored in its database:
{
"id": 123,
"text": "original text"
}
3. For whatever reason, the sender tries to send the same message again, but with amended
text:
{
"id": 123,
"text": "amended text"
}
4. The receiver receives that as well, but since the id field is the same as before, no
action is taken, and this is what the receiver still has in its database:
{
"id": 123,
"text": "original text"
}
The reason for the receiver's behavior is that each distinct message is to be uniquely identified by its id
field, and that if another message is sent with the same id
, it is simply considered a duplicate. Furthermore, trying to change the message's contents like that and resend it with the same ID is invalid behavior on the sender's end.
So technically this is idempotent, which usually leans toward PUT
. However updates are not allowed in any context here, simply inserts. So do we choose PUT
or POST
, or does it even matter? PUT
is supposed to be the full representation of the resource, but if it's simply discarded, is it okay to still return a 2xx
response?
For the purposes of this question, assume that the route used is always of the form <host>/.../message
, never of the form <host>/.../message/{id}
. In that case, I am wondering if being restricted to the <host>/.../message
route scheme automatically means POST
should be used.
Now, in a lot of cases, that would lend itself to the POST verb, since it's idempotent.
This might be a typo on your end, but POST
is NOT idempotent. However PUT
is.
PUT is supposed to be the full representation of the resource, but if it's simply discarded, is it okay to still return a 2xx response?
Totally fine. You requested that a resource with a given id have a given state, and that will be true when the response is sent back.
So do we choose PUT or POST, or does it even matter?
I would say good options are
PUT /messages/{id}
, 201 if the id is new, 204 if the message has the same text as the existing message, 4xx if the text is not the same as the existing messagePOST /messages
, 201 if the id is new, 4xx if the id already existsWith the PUT
the client can see "oh, 2xx, message delivered", or "4xx, I messed up". But you have to have some mechanism (maybe a fingerprint) to quickly check if the text is the same as the initial message.
With the POST
the client would need to inspect an error response to see if they needed to fix something and try again, or if the message was already delivered. If we returned a 2xx (other than 202) from our POST
of an existing message, most clients would interpret that to mean that a new (duplicate) message had been sent.
assume that the route used is always of the form
<host>/.../message
, never of the form<host>/.../message/{id}
An unfortunate requirement, in that case I would use POST
as described above. We technically could use PUT
with that path, but typically that would signal that we want to create/replace the entire message history (you could document around it, but operations that don't do what they look like they should are not typically considered RESTfull).