30

A number of the developers here are having a friendly (some would say religious) discussion about whether a GET request from a RESTful API should return the ID of the requested resource. Let's assume the following GET request:

http://my.api.com/rest/users/23

This currently returns:

{"name": "Jim", "age": 40, "favoriteColor": "blue"}

Note that "id" is missing from the result set.

There are basically 4 camps battling with this issue.

CAMP #1: When callers make the GET request, they already know the ID. Therefore, the result set should not include the ID. If callers need this data to enable UI editing, then callers needs to pass through the ID 23, perhaps adding the member {"id": 23} to the JSON manually.
Folks in Camp #1 also argue that the presence of the ID in the result set would indicate that this value can be modified, which of course it can't.

CAMP #2: Without the ID, the JSON result set can't be used natively for edit/update operations in UI forms. Instead, the AJAX callback mechanism needs to be responsible for passing around ID fields and manually adding these to the result set. This seems klunky and error prone. The UI guys are making the argument that the result set "feels" like it's missing data that should be present, namely the ID.

CAMP #3: These folks are concerned about consistency. If we ever have a collection of user objects returned by an API, these objects MUST include the ID. Therefore, for consistency, the singleton version of a GET should also include the ID.

CAMP #4: These folks are suggesting that the GET request for a user could return meta data in the form of HyperMedia or SelfLinks that would include the ID.

This isn't an esoteric "Who's Right?" argument, either. The approach we take will dictate the shape of our API and affect the work loads of several developers over the new few weeks.

Armchair Bronco
  • 2,367
  • 4
  • 31
  • 44
  • 2
    It's just a personal opinion but I agree with camps 2 and 3. I think the representation should contain all available data, ID included. Particularly when it's a natural ID. This is a very interesting question but I doubt you'll find a simple answer. – toniedzwiedz Jul 02 '12 at 20:10
  • 2
    From the client's perspective, the URI used in the GET *is* the ID. I'd vote for Camp 4 assuming that the "ID" used in the self link is the entire URI, not just the part at the end. – Brian Kelly Jul 03 '12 at 03:29
  • The folks in Camp 4 will be relieved to see that they are not alone. And to confirm, if they were to include the ID, it would be embedded inside a full URI. This means that it will be necessary to parse the URI to extract the ID, but for the Camp 2 folks at least they don't have to manually munge the JSON payload. On the flip side, the Camp 1 adherents are arguing that such a URI would only be a partial implementation of REST Level 3, so for this reason they don't like embedded SelfLinks. – Armchair Bronco Jul 03 '12 at 18:26

2 Answers2

26

This is a matter of opinion, which is not the kind of Question that Stackoverflow loves to see. in any case, I will offer mine.

You are returning the representation of the state of an object or resource. The ID is part of that representation, and therefore ought to be included in the JSON packet. It is a property of the resource. Whether the caller knows the ID or not is not particularly germane to the discussion. CAMP #1 is on shaky ground.

The point you raise about collections is very relevant. Does it make sense to use one representation for the retrieve-1 operation, and another representation for the retrieve-N operation? I think not.

However, the issue you are confronting is more general - what data should be included in the representation that is transferred to clients, and under what circumstances? In some cases the caller simply does not care about a significant subset of the properties. Especially in scenarios where a large set of objects gets retrieved - where the cost to transmit the data is larger in comparison to the base communication cost - you'd like to optimize what is shipped back.

All sufficiently mature REST protocols have an ability to shape the returned data.

For examples, see

  • the Facebook Graph API, http://developers.facebook.com/docs/reference/api/
  • the StackExchange API v2.0 - there is a "filter" object you can pass to precisely shape what gets returned.
  • the CouchDb API - has a map function for every view, which determines what data gets returned. It also has a blanket query parameter, include_docs, which directs the server to include full objects or just metadata. (In some cases you might want only the count of the data, not the actual data.)

Facebook allows you to explicitly specify the fields you want.

enter image description here

The stackexchange API is interesting. They've defined an entirely new type of object to support the shaping. You can use the API to define a "filter" and save it on the server side. Then in your query you pass a filter param with the ID of the filter, and the returned object representations include all the attributes specified in the filter. With no filter you get a "default" subset of fields. To get "all fields" you need to define an all-inclusive filter.

you can see this in action at https://api.stackexchange.com/docs/answers

...and specifically see the filter specification dialog.

enter image description here


There is no single correct way to do things. You need to balance the complexity of the "shaping" feature you support with the cost to develop and the needs of the apps that will use the API.

Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • Thank you for the thoughtful and detailed response. I did think long and hard about posing this question due to the concerns you raised in the first sentence: "This is a matter of opinion, which is not the kind of Question that Stackoverflow loves to see." As the recipient of the dreaded "Peer Pressure" badge I know all too well what can happen with a strictly opinion-based question. In my view, however, this issue has so many programming repercussions between the various Camps that I decided to take the chance. – Armchair Bronco Jul 02 '12 at 23:12
  • 1
    On my end, I've done the work to make Camp #1 happy. I now pass around an array of Key/Value pairs that I can use to augment or supplement the data returned by AJAX-based calls to RESTful services. This works, but it just doesn't feel right. The folks in Camp #1 have agreed to add in the ID if I and the other consumers of the API really want to have this data as part of the GET payload response. – Armchair Bronco Jul 02 '12 at 23:14
  • 2
    Let me ask you this - What if the query is not "Get Person with ID=N" but rather "Get Author of Document with DOCID=Z"? If you follow the advice from Camp #1, you get a person object but you don't know the ID of that person. The "Camp #1" position seems very narrow-minded and untenable. Makes no sense, except in the very simplest and impractical of scenarios. – Cheeso Jul 02 '12 at 23:23
  • I'll pass on your comment. As the principal member of Camp #2, I'm in the wrong Camp to answer your legitimate question. :-) – Armchair Bronco Jul 02 '12 at 23:27
  • 2
    Just accepted this as the answer. In my view, Camps 2 & 3 make the most sense. As noted, I'm am currently passing through the ID manually, then adding this to the JSON in my callback functions. Total hackfest if you ask me. The ID is an integral part of the object I've requested, so it should be returned with the payload regardless of whether I know the ID or not. And there's no point in returning different fields for GET-1 and GET-N calls. Thanks for all the feedback. – Armchair Bronco Jul 05 '12 at 20:40
  • It's not really a subjective question. "REST" has a definition created by the person (Roy Fielding) who coined the term. – Mark E. Haase Feb 26 '15 at 05:07
3

Old question, but since I got here searching for something, here it goes yet another opinion:

First of all, I think the URL should be:

http://my.api.com/rest/Users/23

not

http://my.api.com/rest/getUsers/23

The "GET" lies on the HTTP method, not on the URL. It is just naming, but helps make thing clearer, IMHO.

If you think about this, a resource modification needs to happen in the same URL with a PUT

PUT http://my.api.com/rest/Users/23

In that case, the client needs to keep track of URLs, not IDs. It doesn't matter if the resource returns an ID field. It is up to the client to keep a map of where it was obtained from.

If you try to PUT a resorce to a different URL, say "http://my.api.com/rest/Users/24", a few scenarios would occur:

1) http://my.api.com/rest/Users/24 already exists on the server and server accepts the resource as an update

2) http://my.api.com/rest/Users/24 does not exist on the server and:

a) the server accepts that a User is providing an ID ( 24 ) to a unexisting resource (not recommended) b) the server accepts a PUT as a POST

In a + b, the server would create a new resource.

So:

1) Depending on how much you trust your client, you should probably create a "check-in/check-out" control on the server to avoid overwriting one resource with another (is this RESTful?)

2) You should not accept clients creating IDs (OK to post http://my.api.com/rest/Users/, not http://my.api.com/rest/Users/24 ) and you should not accept PUTs to non-existing resources.

EDIT:

So I believe the resource is identified by its URL, not by the ID. Or, in other words, the resource ID is http://my.api.com/rest/Users/23, not 23.

Makes sense?

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
noderman
  • 1,934
  • 1
  • 20
  • 36
  • I agree that it was a mistake to embed "get" in the URI. I'll see if I can modify the OP. Thanks for the feedback. – Armchair Bronco Feb 07 '14 at 19:55
  • 2
    "So I believe the resource is identified by its URL, not by the ID." This is the core of the answer. Your client shouldn't be constructing URLs, so unless the ID is used for some other purpose, you don't need to include it in the response... ever. Not even in a collection. If you want the client to know where to find singular resource, then provide a URL instead of an ID. See [HATEOAS](http://en.wikipedia.org/wiki/HATEOAS). – Mark E. Haase Feb 26 '15 at 05:08