Rest api design: POST to create with duplicate data, would-be IntegrityError/500, what would be correct?

Skylar Saveland picture Skylar Saveland · Sep 30, 2012 · Viewed 14.2k times · Source

I have a normal, basic REST api like:

/
    GET - list
    POST - create

/<id>
    GET - detail
    PUT - replace
    PATCH - patch
    DELETE - delete

When a POST comes in to /, I usually create an object and make a new id. Some (one) of the fields are (is) required to be unique. So, a POST with such duplicate data could result in:

  1. 500 - IntegrityError
  2. Make it more like a PUT/PATCH to /<id> and update the existing record
  3. Catch/avoid the error and return some sort of 4XX
  4. Something else I'm not thinking of.

1 seems out: the request is either bad or I can deal with it. What is the correct way to handle this situation?

Answer

Tom Howard picture Tom Howard · Sep 30, 2012

@StevenFisher is correct. 409 Conflict is the correct response.

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.

For instance, a GET on / might tell a client that they can create users as follows

HTTP/1.1 200 OK
<users href="/">
    <create href="/" method="post">
        <username type="xs:token" cardinality="required"/>
        <password type="password" cardinality="required"/>
    </create>
    ... other hypermedia controls, like search ...
</users>

Following the hypermedia control and trying to create a user with the username "Skylar Saveland" might result in

HTTP/1.1 409 Conflict
<users href="/">
    <create href="/" method="post">
        <username type="xs:token" cardinality="required" 
                  error="The username 'Skylar Saveland' is already taken. Please select another username"/>
        <password type="password" cardinality="required"/>
    </create>
    ... other hypermedia controls, like search ...
</users>

Similarly, trying to create a user without a password might result in

HTTP/1.1 409 Conflict
<users href="/">
    <create href="/" method="post">
        <username type="xs:token" cardinality="required"/>
        <password type="password" cardinality="required" 
                  error="A password must be specified"/>
    </create>
    ... other hypermedia controls, like search ...
</users>

or you might have multiple errors, e.g.,

HTTP/1.1 409 Conflict
<users href="/">
    <create href="/" method="post">
        <username type="xs:token" cardinality="required"
                  error="The username 'Skylar Saveland' is already taken. Please select another username"/>
        <password type="password" cardinality="required"
                  error="A password must be specified"/>
    </create>
    ... other hypermedia controls, like search ...
</users>

NOTE: An appropriate media type will need to be created to go along with the above, which will explain the structure of the hypermedia controls (including the error attributes on the forms) and define the meaning of the various element names (e.g., users, username, password, etc).