HTTP Status Code when identifier already exists

13,887 views
Skip to first unread message

Peter Monks

unread,
May 29, 2012, 6:15:52 PM5/29/12
to api-...@googlegroups.com
G'day,

We have a scenario where an API client may POST new objects to a collection - for argument's sake let's use a collection of people as an example. Let's assume for this example people are uniquely identified by a single immutable user id, for example "pmonks" (I'm aware of the problems with using pronouns in identifiers - please keep in mind that this is merely an example! ;-)


For simple cases the question of which HTTP status codes to use has unambiguous answers:
Client: POST /people, with id "pmonks"
Server: 201 status code from server, new person object created and available at "/people/pmonks"

Client: GET /people/pmonks
Server: 200 status code from server, body contains data for person "pmonks"

Client: PUT /people/pmonks
Server: 200 status code, existing person object updated


For conflicting updates (PUTs) the answer is obvious too:
"Winning" Client: PUT /people/pmonks
Server: 200 status code, existing person object updated
"Losing" Client: PUT /people/pmonks
Server: 412 status code (we're using etags for optimistic concurrency control)


Now the question is what should happen when two clients simultaneously attempt to create two objects using the same id:
"Winning" Client: POST /people with id "jsmith"
Server: 201 status code from server, new person object created and available at "/people/jsmith"
"Losing" Client: POST /people with id "jsmith"
Server: ???

What HTTP status code should the server return to the "losing" client receive?

Thanks in advance!
Peter

Arlo Belshee

unread,
May 29, 2012, 6:35:39 PM5/29/12
to api-...@googlegroups.com
Previous guidance I've seen suggests that:

* "Create me this resource, and tell me where you put it" is implemented with POST.
* "Create me this resource right here" is implemented as PUT.

According to the HTTP 1.1 RFC:

"The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource."

Creating when you know the key is, in my mind, creating where you know the URI. So I'd implement it as PUT. After all, PUT typically means "cause a future GET to this URI to return this value, without concern to previous server state." That without concern part means that it should handle both creation and update.

Given that, then you don't need to solve your problem. It's all previously-solved cases. A create-PUT is just a PUT with an ETag of "is currently missing" and succeeds iff that resource is in that state.

Of course, this works if you are making a REST API, but will confuse people who are using a more constrained meaning of the verbs (such as ATOM's mapping to CRUD).

Arlo

Peter Monks

unread,
May 29, 2012, 7:01:03 PM5/29/12
to api-...@googlegroups.com
Thanks Arlo.  That still begs the question though - what HTTP status code should the server return, if a create-PUT is executed with an id (a URI) that already exists?

Due to concurrency, it's not sufficient for the client to attempt to GET the URI and confirm it doesn't exist (404) prior to create-PUTting it.

Cheers,
Peter

Rob Mullen

unread,
May 29, 2012, 7:14:45 PM5/29/12
to api-...@googlegroups.com
You basically have two options, and it depends on what you want your client to know.
  1. Return 200 or 201 for success, this is the "more" idempotent approach and we've used this approach in a messaging pub\sub system where the messages can be requeued and order isn't guaranteed and messages may be processed more than once and the message hub is our client.
  2. For another scenario we prefer to give our clients more information about what really happened, in case we return 409 Conflict.  In this particular case we are creating users so our client wants to know if the loginId already exists, so they can trap that condition and return "Hey man, that login id is already in use, please enter a different id.".

Lets Rest

unread,
May 31, 2012, 5:36:11 PM5/31/12
to api-...@googlegroups.com
Like Rob we return 409 if the caller tries to create a resource that already exists
Reply all
Reply to author
Forward
0 new messages