HTTP response code for POST when resource already exists

Viewed 627806

I'm building a server that allows clients to store objects. Those objects are fully constructed at client side, complete with object IDs that are permanent for the whole lifetime of the object.

I have defined the API so that clients can create or modify objects using PUT:

PUT /objects/{id} HTTP/1.1
...

{json representation of the object}

The {id} is the object ID, so it is part of the Request-URI.

Now, I'm also considering allowing clients to create the object using POST:

POST /objects/ HTTP/1.1
...

{json representation of the object, including ID}

Since POST is meant as "append" operation, I'm not sure what to do in case the object is already there. Should I treat the request as modification request or should I return some error code (which)?

18 Answers

It's all about context, and also who is responsible for handling duplicates of requests (server or client or both)


If server just point the duplicate, look at 4xx:

  • 400 Bad Request - when the server will not process a request because it's obvious client fault
  • 409 Conflict - if the server will not process a request, but the reason for that is not the client's fault
  • ...

For implicit handling of duplicates, look at 2XX:

  • 200 OK
  • 201 Created
  • ...

if the server is expected to return something, look at 3XX:

  • 302 Found
  • 303 See Other
  • ...

when the server is able to point the existing resource, it implies a redirection.


If the above is not enough, it's always a good practice to prepare some error message in the body of the response.

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?

  1. We POST to /objects => our target resource is /objects.
  2. 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.
  3. 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.
  4. 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.

In your case you can use 409 Conflict

And if you want to check another HTTPs status codes from below list

1×× Informational

100 Continue
101 Switching Protocols
102 Processing

2×× Success

200 OK
201 Created
202 Accepted
203 Non-authoritative Information
204 No Content
205 Reset Content
206 Partial Content
207 Multi-Status
208 Already Reported
226 IM Used

3×× Redirection

300 Multiple Choices
301 Moved Permanently
302 Found
303 See Other
304 Not Modified
305 Use Proxy
307 Temporary Redirect
308 Permanent Redirect

4×× Client Error

400 Bad Request
401 Unauthorized
402 Payment Required
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Timeout
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Payload Too Large
414 Request-URI Too Long
415 Unsupported Media Type
416 Requested Range Not Satisfiable
417 Expectation Failed
418 I’m a teapot
421 Misdirected Request
422 Unprocessable Entity
423 Locked
424 Failed Dependency
426 Upgrade Required
428 Precondition Required
429 Too Many Requests
431 Request Header Fields Too Large
444 Connection Closed Without Response
451 Unavailable For Legal Reasons
499 Client Closed Request

5×× Server Error

500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
505 HTTP Version Not Supported
506 Variant Also Negotiates
507 Insufficient Storage
508 Loop Detected
510 Not Extended
511 Network Authentication Required
599 Network Connect Timeout Error

I think for REST, you just have to make a decision on the behavior for that particular system in which case, I think the "right" answer would be one of a couple answers given here. If you want the request to stop and behave as if the client made a mistake that it needs to fix before continuing, then use 409. If the conflict really isn't that important and want to keep the request going, then respond by redirecting the client to the entity that was found. I think proper REST APIs should be redirecting (or at least providing the location header) to the GET endpoint for that resource following a POST anyway, so this behavior would give a consistent experience.

EDIT: It's also worth noting that you should consider a PUT since you're providing the ID. Then the behavior is simple: "I don't care what's there right now, put this thing there." Meaning, if nothing is there, it'll be created; if something is there it'll be replaced. I think a POST is more appropriate when the server manages that ID. Separating the two concepts basically tells you how to deal with it (i.e. PUT is idempotent so it should always work so long as the payload validates, POST always creates, so if there is a collision of IDs, then a 409 would describe that conflict).

More likely it is 400 Bad Request

[**6.5.1. 400 Bad Request**][1]


The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

As the request contains duplicate value(value that already exists), it can be perceived as a client error. Need to change the request before the next try.
By considering these facts we can conclude as HTTP STATUS 400 Bad Request.

Since you mentioned that the object create request using post contains the ID of the object, you should make it an idempotent request. Just return the exact same response as a successful create request. Idempotent request makes APIs simpler, eg. now client doesn't have to worry about 2 different cases (success, failure). or client can safely retry the request, in case something goes wrong in connectivity/ server temporarily down.

Error 402, payment required

I.E. this resource already exists but if you give me enough money I'll delete the current one and give it to you :D

...but looking at mozilla's definition of status codes at https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses

as a more serious answer that no one has provided here, what about 451: unavailable for legal reasons. You cannot "legally(by terms and conditions put in place by yourself)" give multiple people access to the same account information

422 is also a good option which is Unprocessable Entity The request was well-formed but was unable to be followed due to semantic errors. since it is a perfectly valid request but due to it being semantically equal to another entry, it cannot be followed.

Related