After having read this and several other, years-long, discussions on status code usage, the main conclusion I came to is that the specifications have to be read carefully, focusing on the terms being used, their definition, relationship, and the surrounding context.
What often happens instead, as can be seen from different answers, is that parts of the specifications are ripped of their context and interpreted in isolation, based on feelings and assumptions.
This is going to be a pretty long answer, the short summary of which is that HTTP 409 is the most appropriate status code to report the failure of an "add new resource" operation, in case a resource with the same identifier already exists. What follows is the explanation why, based solely on what's stated in the authoritative source - RFC 7231.
So why is 409 Conflict
the most appropriate status code in a situation described in the OP's question?
RFC 7231 describes 409 Conflict
status code as follows:
The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource.
The key components here are the target resource and its state.
Target resource
The resource is defined by the RFC 7231 as follows:
The target of an HTTP request is called a "resource". HTTP does not limit the nature of a resource; it merely defines an interface that might be used to interact with resources. Each resource is identified by a Uniform Resource Identifier (URI), as described in Section 2.7 of [RFC7230].
So, when using a HTTP interface, we always operate on the resources identified by URIs, by applying HTTP methods to them.
When our intention is to add a new resource, based on the OP's examples, we can:
- use
PUT
with the resource /objects/{id}
;
- use
POST
with the resource /objects
.
/objects/{id}
is out of interest, because there can be no conflict when using a PUT
method:
The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.
If the resource with the same identifier already exists, it will be replaced by PUT
.
So we'll focus on the /objects
resource and POST
.
RFC 7231 says about the POST
:
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. For example, POST is used for the following functions (among others): ... 3) Creating a new resource that has yet to be identified by the origin server; and 4) Appending data to a resource's existing representation(s).
Contrary to how the OP understands POST
method:
Since POST is meant as "append" operation...
Appending data to a resource's existing representation is just one of the possible POST
"functions". Moreover, what the OP actually does in the provided examples, is not directly appending data to the /objects
representation, but creating a new independent resource /objects/{id}
, which then becomes part of the /objects
representation. But that's not important.
What's important is the notion of the resource representation, and it brings us to...
Resource state
RFC 7231 explains:
Considering that a resource could be anything, and that the uniform interface provided by HTTP is similar to a window through which one can observe and act upon such a thing only through the communication of messages to some independent actor on the other side, an abstraction is needed to represent ("take the place of") the current or desired state of that thing in our communications. That abstraction is called a representation [REST].
For the purposes of HTTP, a "representation" is information that is intended to reflect a past, current, or desired state of a given resource, in a format that can be readily communicated via the protocol, and that consists of a set of representation metadata and a potentially unbounded stream of representation data.
That's not all, the specification continues to describe representation parts - metadata and data, but we can summarize that a resource representation, that consists of metadata (headers) and data (payload), reflects the state of the resource.
Now we have both parts needed to understand the usage of the 409 Conflict
status code.
409 Conflict
Let's reiterate:
The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource.
So how does it fit?
- We
POST
to /objects
=> our target resource is /objects
.
- OP does not describe the
/objects
resource, but the example looks like a common scenario where /objects
is a resource collection, containing all individual "object" resources. That is, the state of the /objects
resource includes the knowledge about all existing /object/{id}
resources.
- When the
/objects
resource processes a POST
request it has to a) create a new /object/{id}
resource from the data passed in the request payload; b) modify its own state by adding the data about the newly created resource.
- When a resource to be created has a duplicate identifier, that is a resource with the same
/object/{id}
URI already exists, the /objects
resource will fail to process the POST
request, because its state already includes the duplicate /object/{id}
URI in it.
This is exactly the conflict with the current state of the target resource, mentioned in the 409 Conflict
status code description.