Designing an API, we’ve come up against the question of whether a PUT payload should contain the ID of the resource being updated.
This is what we currently have:
PUT /users/123 Payload: {name: "Adrian"}
Our route code extracts the ID from the URI and continues on with the update.
The first users of our API are questioning why we don’t allow ID in the payload:
PUT /users/123 Payload: {id: 123, name: "Adrian"}
The reason we didn’t allow it is because the ID is duplicated, in the payload and URI.
Thinking about this some more, we are coupling the resource to the URI.
If the URI doesn’t have the ID, the payload will need to be amended:
PUT /no/id/here Payload: {name: "Adrian"} < What user???
Are there any reasons not to?
You are supposed to couple the Uniform Resource Identifier to the resource.
When REST is implemented with HTTP, you use GET to retrieve the current value of the resource and PUT to set a new value. The GET does not have a payload, so the resource has to be identified by the URI. And the PUT is logically done to the same URI and the payload should look exactly as what you want the next GET to return.
You can use POST to different URI, but it would only make less sense as it would be unnecessarily asymmetrical to the GET. POST to common URI could only make sense for creating new resources (POST /users/new
, payload: {name: "Adrian"}
, response {id: 345, name: "Adrian"}
), but that’s not idempotent and therefore should be avoided if you are striving for REST¹. Instead you should reserve ID with one call and then use PUT to set the new ID; that is fault-tolerant, because if the first request fails, the ID reservation can time out eventually and the PUT
is idempotent. Or use client-generated UUID.
¹ The definition of REST does not say anything about idempotence, so I can’t really claim it is not REST if you have non-idempotent operations. That does not change the fact that sticking to idempotent requests makes things more reliable without complicating them and is therefore recommended.
6
Thinking about this some more, we are coupling the resource to the
URI.If the URI doesn’t have the ID, the payload will need to be amended:
PUT /no/id/here Payload: {name: “Adrian”} < What user???
Are there any reasons not to?
The answer to this question depends on whether you want to allow the client to change the ID?
If the client can change the ID, via a PUT, then the URI for the resource will change, and you should provide a 301 Moved Permanently any time a resource accesses the old URI.
So for example you start with a resource at
/users/123
and the client PUTs the following onto the resource
{id: 222, name: "Adrian"}
the resource has been updated and its URI is now
/users/222
The Location
field in the PUT response should contain the new URI, and if you go to /users/123
you should get a 301
response with the Location field pointing to the new /users/222
resource.
In most cases though you don’t actually want the client to be able to change the ID, as this can get pretty messy pretty quickly. In that case the ID is something only the server can change, and you should leave it out of the PUT body, as the client cannot update this state.
If you PUT a require to a different URI on the same resource, say
/users/adian_lync
then if that resource does not exist the server should create it and create and ID when it is doing it
4