views:

167

answers:

4

For the site I am working on, we are in the process of improving our URLs for one type of resource - specifically, moving away from numerical IDs toward unique, descriptive strings. A similar example would be switching from identifying users by numerical database ID to identifying them by username (not our specific case, but analagous). So a URL to access a user's information used to look like:

/users/48573

And now it looks like

/users/thisisausername.

The only problem is that we still need to be able to fetch them through numerical IDs somehow, for legacy consumers of the API. We don't need the REST URLs themselves to redirect (e.g. /users/48573 should not redirect to /users/thisisausername), we just need a method to obtain the right data using the old identifier. The solution should either provide an alternate way of accessing the user information (which conveniently includes the new identifier, username) by ID, or of accessing just the username by ID. Some possible solutions might be:

  • Using a node to specify some alternate method of identification, e.g. /users/byid/48573
  • Using a query parameter to specify some alternate method of identification, e.g. /users/48573?fetchby=id or /users/48573?byid=true
  • Treating username-by-id as another resource, e.g. /identifiers/username/48573

Which of these (if any) is closest to proper REST? How would you deal with the problem?

A: 

Your first option is probably the best.

Searching for users by ID:

/users/id/48573

Searching for users by short name:

/users/name/thisisausername

If they leave out that path parameter, you could always default to your new short username format.

Another option that I have seen quite a bit is to use query parameters like the following:

/users?id=48573
/users?name=thisisausername

I think the first looks a bit cleaner and more readable.

Chris Dail
+1  A: 

Can't you just write a wrapper?

if (Regex.Match("^[0-9]+$", user).Success)
  // get by userid
else
 // get by username

But then you have the problem with a user signs up as "666" as their username. If you disallow all numbers in your alternative key ... it would be handy to implement both. But different URL's for the same resource is confusing imho and according to the RESTful Uri reccomendations I have seen adding /id/ /name/ are not preferred for a resource. Since they are technically "arguments" to a "find" mechanism they don't have much to do with the resource.

http://microformats.org/wiki/rest/urls

Chad Grant
Exactly - the reason we can't do that is the collision you are talking about. Our specific situation is dealing with user-created groups for which users can create their own slugs, and we run into this problem when a user wants to create a Tommy Tutone fan group and call it 8675309. It's funny that you bring up versioning the domain. We have v1 in our URIs, and that's actually never changed despite going through multiple iterations! Changing the interface hasn't become a problem yet, since our API's only consumers are internal to the company - it's not public (yet).
Kelly Ellis
For versioning REST Web Services if found this very good: http://barelyenough.org/blog/2008/05/versioning-rest-web-services/
A: 

I'd consider qualifying the string with an optional suffix:

/users/48573/id

/users/48573/name

If you receive a string without the suffix:

/users/48573

then you check the string and see if it's an ID or Name.

If you only get a valid ID but not a name then it's a retrieval by ID equivalent to:

/users/48573/id

If you only get a name back then it's a retrieval by Name equivalent to:

/users/48573/name

If you can retrieve the value by ID or Name then you return a 300 response error and return links to both possiblities to the client:

/users/48573/id

/users/48573/name

The legacy consumers continue to work 'as is' except for the occasional occurence of duplicate ID/name pairs where they receive the new 300 response error.

Paul Morgan
This is not REST at all. This is just RPC.
Wahnfrieden
A: 

Your API is not RESTful if this is an issue. To quote Roy Fielding:

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].

A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations. The transitions may be determined (or limited by) the client’s knowledge of media types and resource communication mechanisms, both of which may be improved on-the-fly (e.g., code-on-demand). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]

Wahnfrieden