11

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?

14

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.

  • 3
    As far as I know a POST request does not have to be idempotent. So I see no problem with posting to /users (no need to add 'new'). – lex82 Apr 22 '17 at 7:30
  • thanks for your reply. I see that it is a nice feature to have idempotency for all requests but who says this is required for a REST API? Certainly many APIs that call themselves RESTful allow non-idempotent POST requests (especially when the server generates ids for new resources). But even if you apply a very strict definition of REST, I don't see which architectural constraint ist violated with non idempotent POSTs like the example above. – lex82 Apr 22 '17 at 11:50
  • I can certainly image POST requests that violate a constraint. I just don't see why posting a resource into a collection and letting the server decide on its id is a violation of REST constraints. – lex82 Apr 22 '17 at 11:53
  • Let us continue this discussion in chat. – lex82 Apr 22 '17 at 12:46
  • 3
    The whole idea of POST is not to be idempotent.... – EralpB May 19 '17 at 8:34
1

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

  • 1
    The reason for us questioning the placement of the ID in the payload was due to Backbone.js passing the ID in a PUT request by default. We can stop it from happening, but now I want to know why that's the default behaviour. – Adrian Lynch Nov 28 '14 at 17:42
  • 1
    Afraid I'm not familiar with Backbone.js. Seem unnecessary if the ID is included in the URL as well. Perhaps an oversight on the part of the devs – Cormac Mulhall Nov 28 '14 at 17:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.