175

Let's say I want to have a RESTful resource for people, where the client is able to assign ID.

A person looks like this: {"id": <UUID>, "name": "Jimmy"}

Now, how should the client save (or "PUT") it?

  1. PUT /person/UUID {"id": <UUID>, "name": "Jimmy"} - now we have this nasty duplication that we have to verify all the time: Does the ID in body match the one in path?
  2. Asymmetric representation:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
  3. No IDs in body - ID only in location:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID returns {"name": "Jimmy"}
  4. No kind of POST seems like a good idea since the ID is generated by the client.

What are the common patterns and ways to solve it? IDs only in location seems like the most dogmatically correct way, but it also makes the practical implementation harder.

Community
  • 1
  • 1
Konrad Garus
  • 53,145
  • 43
  • 157
  • 230

10 Answers10

106

There is nothing wrong in having different read/write models: the client can write one resource representation where after the server can return another representation with added/calculated elements in it (or even a completely different representation - there is nothing in any spec against that, the only requirement is that PUT should create or replace the resource).

So I would go for the asymmetric solution in (2) and avoid the "nasty duplication check" on the server side when writing:

PUT /person/UUID {"name": "Jimmy"}

GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
Jørn Wildt
  • 4,274
  • 1
  • 21
  • 31
  • 3
    And if you apply typing (static or dynamic) you can't painlessly have models without ID... So it's much easier to remove ID from URL for PUT requests. It won't be "restful" but it will be correct. – Ivan Kleshnin Sep 23 '16 at 09:19
  • 3
    Keep additional TO without `id` along with TO with id and entity and additional converters and too big overhead for programmers. – Grigory Kislin May 30 '17 at 11:07
  • 1
    What if I get the ID from BODY ex.: PUT /person { "id": 1, "name": "Jimmy" }. Would that be a bad practice? – Bruno Santos Sep 13 '19 at 09:17
  • Putting the ID in the body would be fine. Use a GUID for ID instead of an integer - otherwise you run the risk of duplicate IDs. – Jørn Wildt Sep 13 '19 at 14:18
  • 3
    This is wrong. See my answer. PUT must contain the entire resource. Use PATCH if you want to exclude the id and update just parts of the record. – CompEng88 Mar 04 '20 at 21:50
  • Agree with 'nothing wrong', but fix "there is nothing in any spec against that, the only requirement is that PUT...". Actually, 2014's RFC 7231 (one of RFCs that obsolete the 1999's RFC 2616) mentions [here](https://tools.ietf.org/html/rfc7231#section-4.3.4) that server can cause `"transformation applied to the body"`. There's even a mechanism for client to `"allows a user agent to know when the representation body it has in memory remains current"`, i.e. the server `"MUST NOT send ... an `ETag` or `Last-Modified` ... unless the request's representation was saved without any transformation"`. – Slawomir Brzezinski Oct 26 '20 at 07:55
  • Why should `GET` return the id? It's already known and in the URL... – john16384 Mar 17 '21 at 15:50
  • @john16384 true, but then the entity is would be w/o ID and thus wouldn't fully represent itself. So the client would need some way tag the ID on the object anyhow in order to send it to the server for CRUD stuff , while a map would work the ID is kept separate and involves extra work. – elonderin May 29 '21 at 16:12
  • @elonderin the ID is not part of the representation (two entities can be considered the same if all fields match but not their ID's). The client needs to remember the entire URL together with the result, the ID is not enough; returning the ID as an "id" just encourages clients to do bad practices like using a base URL and appending an ID to that. – john16384 May 30 '21 at 20:08
  • @john16384 ok, that might be the case if you can use the same representation of the entity against different systems, e.g. in an micro-service environment where either the ID is local to each MS OR the states may differ and you must handle that sort of thing. But if you have an ID which is also the business key or you just work against 1 system, then this is overkill that i would avoid it and just stick the ID in the JSON. KISS. – elonderin Jun 01 '21 at 17:26
  • Update to my comment: Keep additional TO without id: for Swagger could be soldev by `@Schema(accessMode = Schema.AccessMode.READ_ONLY)` for `id` – Grigory Kislin Sep 29 '21 at 13:04
35

If it is a public API you should be conservative when you reply, but accept liberally.

By that I mean, you should support both 1 and 2. I agree that 3 doesn't make sense.

The way to support both 1 and 2 is to get the id from the url if none is supplied in the request body, and if it is in the request body, then validate that it matches the id in the url. If the two do not match, then return a 400 Bad Request response.

When returning a person resource be conservative and always include the id in the json, even though it is optional in the put.

Jay Pete
  • 4,123
  • 4
  • 35
  • 51
  • 6
    This should be the accepted solution. An API should always be user-friendly. It should be optional in the body. I shouldn't receive an ID from a POST and then have to make it undefined in a PUT. Also, the 400 response point made is right on. – Michael Sep 11 '18 at 01:12
  • About 400 code see also https://softwareengineering.stackexchange.com/questions/329229/should-i-return-an-http-400-bad-request-status-if-a-parameter-is-syntactically discussion. In short a 400 code is not inappropriate, just less specific, compare to 422. – Grigory Kislin Apr 10 '20 at 12:51
  • 5
    I kinda disagree, tho no downvote. I think an API should be strict. If I want to do something with an API then it should be one way (and as per docs), not optional e.g. URI vs body. If we make it loose then it's more code and maintenance on the API from allowing multiple options - if body is empty then check URI and vice versa. Also, it breaks consistency, not only in the way the client calls and the server receives on this API, but across the internet as a whole. It'd be better if we just all agreed on one way for these things and everyone stick to it. Less subjective advice/guides – James Jan 02 '22 at 19:43
  • 5
    After many years of working with APIs my view has changed on this, and I agree with you on this @James. Doing it the way you suggest makes it much more explicit to the client what it is about to do. – Jay Pete Mar 30 '22 at 06:23
  • Does this answer change in any way for the more traditional case where the server assigns the ID (not the client)? – chrisinmtown Apr 14 '22 at 22:00
  • @chrisinmtown If the server is assigning an ID, then that is likely a POST of a new thing, not a PUT to update an existing thing. – DBK Jul 02 '23 at 16:36
  • 400 means that the request was not understood. Here, the request is understood but the server is refusing to fulfill it (because the IDs don't match). That would be a 403. – Greg Brown Jul 07 '23 at 12:18
  • @Michael The original question was about PUT, not POST. In both example requests above, the caller is providing the ID, not the server. – Greg Brown Jul 07 '23 at 12:18
12

Just FYI, the answers here are wrong.

TLDR; If you're using PUT, you should have the id in the body. If you are using PATCH, you do not need the id in the body.

See:

https://restfulapi.net/rest-api-design-tutorial-with-example/

https://restfulapi.net/rest-put-vs-post/

https://restfulapi.net/http-methods/#patch

PUT

Use PUT APIs primarily to update existing resource (if the resource does not exist, then API may decide to create a new resource or not). If a new resource has been created by the PUT API, the origin server MUST inform the user agent via the HTTP response code 201 (Created) response and if an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request.

If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those entries SHOULD be treated as stale. Responses to this method are not cacheable.

Use PUT when you want to modify a singular resource which is already a part of resources collection. PUT replaces the resource in its entirety. Use PATCH if request updates part of the resource.

PATCH

HTTP PATCH requests are to make partial update on a resource. If you see PUT requests also modify a resource entity so to make more clear – PATCH method is the correct choice for partially updating an existing resource and PUT should only be used if you’re replacing a resource in its entirety.

So you should use it in this way:

POST    /device-management/devices      : Create a new device
PUT     /device-management/devices/{id} : Update the device information identified by "id"
PATCH   /device-management/devices/{id} : Partial-update the device information identified by "id"

RESTful practices indicate that it shouldn't matter what you PUT at /{id}--the content of the record should be updated to the one provided by the payload--but GET /{id} should still link to the same resource.

In other words, PUT /3 may update to payload id to 4, but GET /3 should still link to the same payload (and return the one with id set to 4).

If you are deciding that your API requires the same identifier in the URI and the payload, it's your job to make sure it matches, but definitely use PATCH instead of PUT if you are excluding the id in the payload that should be there in its entirety. This is where the accepted answer got it wrong. PUT must replace the entire resource, where-as patch may be partial.

CompEng88
  • 1,336
  • 14
  • 25
  • In your paragraph: "In other words, PUT /3 may update to payload id to 4, but GET /3 should still link to the same payload (and return the one with id set to **4**)." - did you mean **3** instead of **4** in the last id? – mlst Nov 06 '20 at 17:37
  • Re: _"use PATCH instead of PUT if you are excluding the id"_. **This is ill informed.** The 2014's RFC 7231 (one of RFCs that obsolete the 1999's RFC 2616) mentions [here](https://tools.ietf.org/html/rfc7231#section-4.3.4) that server can have `transformation applied to the body`. There's even a mechanism for client that `allows a user agent to know when the representation body it has in memory remains current`, which is: the server `MUST NOT send ... an ETag or Last-Modified ... unless the request's representation was saved without any transformation`. – Slawomir Brzezinski Nov 08 '20 at 20:06
  • Also see [this SO question+answer from other users](https://stackoverflow.com/a/5686942/4356868) containing justification why it's appropriate to use PUT. It nicely summarizes _"The important thing, is to understand what was the intent of the client request. The client intended to completely replace the contents of resource with the values passed."_ – Slawomir Brzezinski Nov 08 '20 at 20:10
  • The OP asked about RESTful practices. PATCH allows you to update parts of the resource that are present, where PUT replaces all parts of the resource or inserts the resource if it is not present. I would not use PUT if you exclude part of the resource. That is what PATCH is for. – CompEng88 Jan 05 '21 at 17:45
  • @mlst This wasn't a mistake. If you PUT /3 with id 4 inside the payload, your server should update the record with the full content of the PUT. That's because /3 does not have to be the same meaning as the id in the payload. If /3 is supposed to be the same and not change bc your app said so, you should reject the PUT. Some could id the res via a UUID for example (PUT /:UUID) and have a different int id in the payload. If that's the case, after you PUT /:UUID a new payload id, calling GET /:UUID returns exactly what you PUT. REST does not explicitly tie the res to a db id, but it could. – CompEng88 Jan 05 '21 at 18:02
  • a lot of text and no answer on question: does id needed in body – Grigory Kislin Sep 20 '21 at 15:15
  • If you're using PUT, you should have the id in the body. If you are using PATCH, you do not need the id in the body. – CompEng88 Sep 20 '21 at 19:27
  • *RESTful practices indicate that it shouldn't matter what you PUT at /{id}* - so why using `{id}` in the first place? Why not having a bare URL? (without the `/{id}`) – WoJ Jul 20 '23 at 15:47
  • PUT is used to update or insert a resource. That resource is identified (in this specific case) with {id} in the URI. Use POST to insert a resource without a resource id. POST a payload and it should insert it multiple times and thus produce multiple URIs, where as, if you PUT a resource, it will affect the singular URI. – CompEng88 Jul 25 '23 at 22:30
10

One solution to this issue involves the somewhat confusing concept of "Hypertext As The Engine Of Application State," or "HATEOAS." This means that a REST response contains the available resources or actions to be performed as hyperlinks. Using this method, which was part of the original conception of REST, the unique identifiers/IDs of resources are themselves hyperlinks. So, for example, you could have something like:

GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}

Then, if you want to update that resource, you could do (pseudocode):

updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)

One advantage of this is that the client doesn't have to have any idea about the server's internal representation of User IDs. The IDs could change, and even the URLs themselves could change, as long as the client has a way to discover them. For example, when getting a collection of people, you could return a response like this:

GET /people
{ "people": [
    "/person/1",
    "/person/2"
  ]
}

(You could, of course, also return the full person object for each person, depending on the needs of the application).

With this method, you think of your objects more in terms of resources and locations, and less in terms of ID. The internal representation of unique identifier is thus decoupled from your client logic. This was the original impetus behind REST: to create client-server architectures that are more loosely coupled than the RPC systems that existed before, by using the features of HTTP. For more information on HATEOAS, look at the Wikipedia article as well as this short article.

bthecohen
  • 486
  • 3
  • 8
8

While it's Ok to have different representations for different operations, a general recommendation for PUT is to contain the WHOLE payload. That means that id should be there as well. Otherwise, you should use PATCH.

Having said that, I think PUT should mostly be utilised for updates and the id should always be passed in the URL as well. As a result of that, using PUT to update the resource identifier is a bad idea. It leaves us in an undesirable situation when id in the URL can be different from the id in the body.

So, how do we resolve such a conflict? We basically have 2 options:

  • throw a 4XX exception
  • add a Warning(X-API-Warn etc) header.

That's as close as I can get to answering this question because the topic in general is a matter of opinion.

yuranos
  • 8,799
  • 9
  • 56
  • 65
7

In an insert you do not need to add the id in the URL. This way if you send an ID in a PUT you may interpreted as an UPDATE to change the primary key.

  1. INSERT:

    PUT /persons/ 
      {"id": 1, "name": "Jimmy"}
    HTTP/1.1 201 Created     
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
    
    GET /persons/1
    
    HTTP/1.1 200 OK
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}  
    
  2. UPDATE

    PUT /persons/1 
         {"id": "2", "name": "Jimmy Jr"} - 
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="filled_by_server"}
    
    GET /persons/2 
    
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="updated_by_server"}
    

The JSON API uses this standard and solves some issues returning the inserted or updated object with a link to the new object. Some updates or inserts may include some business logic that will change additional fields

You will also see that you can avoid the get after the insert and update.

Community
  • 1
  • 1
borjab
  • 11,149
  • 6
  • 71
  • 98
3

This has been asked before - the discussion is worth a look:

Should a RESTful GET response return a resource's ID?

This is one of those questions where it's easy to get bogged down into debate around what is and is not "RESTful".

For what it's worth, I try to think in terms of consistent resources and not change the design of them between methods. However, IMHO the most important thing from a usability perspective is that you are consistent across the entire API!

Community
  • 1
  • 1
Ben Morris
  • 585
  • 3
  • 6
3

There is nothing bad in using different approaches. but i think the best way is the solution with 2nd.

 PUT /person/UUID {"name": "Jimmy"}

 GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

it is mostly used in this way even the entity framework use this technique when the entity is added in dbContext the class without the generated ID is ID generated by reference in Entity Framework.

Shan Khan
  • 9,667
  • 17
  • 61
  • 111
1

You may need to look into PATCH/PUT request types.

PATCH requests are used to update a resource partially whereas in PUT requests, you have to send the entire resource where it gets overridden on the server.

As far as having an ID in the url is concerned, I think you should always have it as it is a standard practice to identify a resource. Even the Stripe API works that way.

You can use a PATCH request to update a resource on the server with ID to identify it but do not update the actual ID.

Noman Ur Rehman
  • 6,707
  • 3
  • 24
  • 39
1

I'm looking at this from a JSON-LD/ Semantic Web point of view because that's a good way to go to achieve real REST conformance as I have outlined in these slides. Looking at it from that perspective, there is no question to go for option (1.) as the ID (IRI) of a Web resource should always be equal to the URL which I can use to look-up/ dereference the resource. I think the verification is not really hard to implement nor is it computationally intens; so I don't consider this a valid reason for going with option (2.). I think option (3.) is not really an option as POST (create new) has a different semantics than PUT (update/ replace).

vanthome
  • 4,816
  • 37
  • 44