3

I am trying to define conventions on PUT requests on a project I'm working on and I cannot decide which of these two options is the most appropriate, or if it is a matter of personal preference. I have seen multiple articles on REST best practices and it seems that the id is always implied as necessary as part of the url for PUT requests.

Goal: Update a resource via a REST API.

Options

  1. PUT collection/{id} - no id in the body
  2. PUT collection/ - no id in the URL, only in the body

Questions

  1. Which convention should I use?
  2. Why?
  3. Are there any generally applicable pros and cons for each option?
panda
  • 315
  • 4
  • 15
  • Does this answer your question? [Can I PUT without an ID?](https://stackoverflow.com/questions/43391191/can-i-put-without-an-id) – Roman Vottner Mar 19 '20 at 17:40
  • @RomanVottner the question you mention is asking if not including the id at all in the request (neither in the url nor in the body) is OK. My question is between having the id in the url XOR the body :) – panda Mar 19 '20 at 17:43

3 Answers3

4

It's important to make sure that you understand the semantics of PUT

A successful PUT of a given representation would suggest that a subsequent GET on that same target resource will result in an equivalent representation being sent in a 200 (OK) response.

In other words

PUT /collection

means "replace the representation of /collection with the representation included in this message".

So that's the right request to use if you are trying to modify the representation of the collection resource itself, and the wrong thing to do if you are trying to modify the representation of something else.

For example, if the representation of the collection was a list of descriptions of its elements, then you could use PUT to replace the list with a different one.

GET /collection

200 OK

[1, 2, 3]

PUT /collection

[4, 5]

204 No Content

GET /collection

[4, 5]

In other words, from the point of view of REST, "collections" use the exact same messages, with the same semantics, as every other resource.

If what you are trying to describe is a change to some other resource, then you use the identifier for that resource

PUT /collection/1

Now, should that identifier "1" be in the representation? It depends - but here is the basic rule: consumers (and intermediary components) should never be trying to extract information from the URI, only from the representations. So if your consumer use cases need that "1", then it needs to appear in the representation of the resource.

PUT is, in this sense, just the dual of GET -- if GET needs to include information in the representation provided by the response, then PUT needs to include that same information in the representation provide by the request.

It may be useful to think of file contents - we put the information that the reader needs into the file, the file name/path just tells the reader how to find it. Or if you think about a Hash/Map/Dictionary -- the key is used to get values in and out of the map, but the information is in the value.

If I am not planning to support complete collection replacement

... then PUT /collection is wrong, because we have all agreed (via RFC 7231) that PUT means "replacement".

It's okay to use POST, instead of PUT, if you are trying to manipulate /collection in some non standardized way.

If you are intending to manipulate the representation of the collection, but you want to describe inserts and removals rather than sending the entire representation every time, you could use PATCH with some sort of patch document that describes your changes.

Community
  • 1
  • 1
VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • I agree and like 'the basic rule of consumers' and this is what actually got me thinking in the first place. If I am not planning to support complete collection replacement, would in your opinion not including the id in the url be an OK approach? The issue if i follow the 'basic rule' is that if I include it in the URL i need to do extra validation and I would like to avoid even introducing this error prone request format. – panda Mar 19 '20 at 18:09
  • I've attempted to address your question with an update; let me know if it helps. – VoiceOfUnreason Mar 19 '20 at 20:17
2

REST relies on the semantics of its transport layer, HTTP in most cases. HTTP at its heart is just a remote document management system that just takes care of sending files from one location to an other in a stateless manner. As Jim Webber pointed out, any business rules concluded are basically just a side-effect of the actual document management.

As such, you should view your task firstly in the sense of a regular document management. You put a file up somewhere and replace any existing representation. This is what PUT actually means. An ID within a document is therefore not the best idea as long as it is not part of the actual document's content.

Whether the URI contains a separate identifier or not is not of importance in a REST architecture. More formally, the whole URI is the identifier of a resource itself regardless whether you put some further ID into the URI or not. A URI neither should tell a client anything about the resource or its semantics nor about any hierarchy. Content type negotiation and link relations are there for.

Fielding even stated that

A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC’s functional coupling]. (Source)

The reason why many developers put IDs into their URIs is basically that on the server side they tokenize the URI and use respective tokens to perform a DB lookup on the respective segments. A reason why not to put i.e. productIDs into URIs is that when the ID changes, for whatever reason, the URI changes as a consequence event though the actual product remains perfectly the same. I.e. think of product A000123 that suddenly is referred to as P12300001 but nothing else changed. This could have happened due to a company merger or the like (have had such an incident in the past i.e.). In a REST architecture an URI such as /products/A000123 and /products/P12300001 refer to two different resources, and therefore two different products, even though they might return identical content. If instead of a product identifier a further, arbitrary UUID would have been used that leads to an internal mapping of that UUID to a productId the URI could have remained the same as basically the product also remained the same.

I'd go so far to reframe your question to: "Does your client really need the ID?". In a perfect REST architecture a server would teach a client at any step of the process what the client is able and allowed to do. As REST is basically just a generalization of the concepts used on the Web, the whole design can be modeled as if the interaction should happen on the Web and then use the same concepts for application interactions. Think of an arbitrary client, human or machine-driven, that should order some item from Amazon i.e. The client asks Amazon for a list of products matching some search criteria, the server responds with a page containing links to more or less matching products a client can drill down further to learn the details of each product. If interested the client can add a product to its shopping cart by clicking a link. It actually doesn't care how the URI looks like, it just invoked it as some metadata (text or link relation name) stated that this will add the product to the cart. At no point the client is really interested in the actual product number, so why add it other than to give a client (human interactor) a possibility to use it for referral reasons in email interactions with the support or the like.

In the end, whether you use IDs within URIs or the body payload, is your personal choice. Depending on your use-case exposing IDs to clients might not even be necessary as the whole interaction model used in REST (and on the Web) just use URIs to retrieve the next "page" of data and the hint on whether to use that URI is kept in metadata accompanying the URI, such as a summary text of the links content, as in the case of HTML links, or as part of link relation names that are either defined by media-types, by Web linking extensions (such as Dublin Core) or by a standardization authority.

Community
  • 1
  • 1
Roman Vottner
  • 12,213
  • 5
  • 46
  • 63
0

PUT replaces the resource at the specified uri.

So if you PUT collection, I would expect the entire collection to be overwritten.

So this is not really an opinion matter, in this case it's simply the wrong thing.

Evert
  • 93,428
  • 18
  • 118
  • 189
  • thank you for your answer. Is this defined/recommended in an standard? or is it your expectation based on your perspective on what PUT without an id would do? In my case I don't want to offer complete collection replacement – panda Mar 19 '20 at 17:59
  • Yes this is in the HTTP standard. – Evert Mar 19 '20 at 18:18