tags:

views:

4594

answers:

7

I'm looking for a reasonable way to represent searches as a RESTful URLs.

The setup: I have two models, Cars and Garages, where Cars can be in Garages. So my urls look like:

/car/xxxx
  xxx == car id
  returns car with given id

/garage/yyy
  yyy = garage id
  returns garage with given id

A Car can exist on its own (hence the /car), or it can exist in a garage. What's the right way to represent, say, all the cars in a given garage? Something like:

/garage/yyy/cars     ?

How about the union of cars in garage yyy and zzz?

What's the right way to represent a search for cars with certain attributes? Say: show me all blue sedans with 4 doors :

/car/search?color=blue&type=sedan&doors=4

or should it be /cars instead?

The use of "search" seems inappropriate there - what's a better way / term? Should it just be:

/cars/?color=blue&type=sedan&doors=4

Should the search parameters be part of the PATHINFO or QUERYSTRING?

In short, I'm looking for a good guide/tutorial for cross-model REST url design, and for search.

[Update] I like Justin's answer, but he doesn't cover the multi-field search case:

/cars/color:blue/type:sedan/doors:4

or something like that. How do we go from

/cars/color/blue

to the multiple field case?

+16  A: 

My advice would be this:

/garages
  Returns list of garages (think JSON array here)
/garages/yyy
  Returns specific garage
/garage/yyy/cars
  Returns list of cars in garage
/garages/cars
  Returns list of all cars in all garages (may not be practical of course)
/cars
  Returns list of all cars
/cars/xxx
  Returns specific car
/cars/colors
  Returns lists of all posible colors for cars
/cars/colors/red,blue,green
  Returns list of cars of the specific colors (yes commas are allowed :) )

Edit:

/cars/colors/red,blue,green/doors/2
  Returns list of all red,blue, and green cars with 2 doors.
/cars/type/hatchback,coupe/colors/red,blue,green/
  Same idea as the above but a lil more intuitive.
/cars/colors/red,blue,green/doors/two-door,four-door
  All cars that are red, blue, green and have either two or four doors.

Hopefully that gives you the idea. Essentially your Rest API should be easily discoverable and should enable you to browse through your data. Another advantage with using URLs and not query strings is that you are able to take advantage of the native caching mechanisms that exist on the web server for HTTP traffic.

Here's a link to a page describing the evils of query strings in REST: http://209.85.173.104/search?q=cache:SQSlLYecB0gJ:rest.blueoxen.net/cgi-bin/wiki.pl%3FQueryStringsConsideredHarmful+rest+with+query+strings&hl=en&ct=clnk&cd=1&gl=us&client=safari

I used Google's cache because the normal page wasn't working for me here's that link as well: http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful

Justin Bozonier
Thanks for the detailed answer. On the last one, what if I want to search by both color and number of doors? /cars/colors/red,blue,green/doors/4That doesn't seem right.
Parand
Commas in the URL don't feel right to me, but still valid rest. I think it is just a paradigm shift.
Justin Bozonier
I don't like this suggestion. How would you know the difference between `/cars/colors/red,blue,green` and `/cars/colors/green,blue,red` ? The path element of the URI should be hierarchical, and I don't really see that being the case here. I think this is a situation where the query-string is the most appropriate choice.
troelskn
This is RPC, not REST.
Wahnfrieden
My 2 cents: use semi-colon where the order of arguments are important (e.g. GPS coordinates), e.g. host.com/maps/gps/12.67;45.67
opyate
+4  A: 

Justin's answer is probably the way to go, although in some applications it might make sense to consider a particular search as a resource in its own right, such as if you want to support named saved searches:

/search/{searchQuery}

or

/search/{savedSearchName}
Peter Hilton
A: 

Though I like Justin's response, I feel it more accurately represents a filter rather than a search. What if I want to know about cars with names that start with cam?

The way I see it, you could build it into the way you handle specific resources:
/cars/cam*

Or, you could simply add it into the filter:
/cars/doors/4/name/cam*/colors/red,blue,green

Personally, I prefer the latter, however I am by no means an expert on REST (having first heard of it only 2 or so weeks ago...)

+10  A: 

Although have the parameters in the path has some advantages, there are, IMO, some outweighing factors.

  • Not all characters needed for a search query are permitted in a URL. Most punctuation and Unicode characters would need to be URL encoded as a query string parameter. I'm wrestling with the same problem. I would like to use XPath in the URL, but not all XPath syntax is compatible with a URI path. So for simple paths, /cars/doors/driver/lock/combination would be appropriate to locate the 'combination' element in the driver's door XML document. But /car/doors[id='driver' and lock/combination='1234'] is not so friendly.

  • I think there is a difference between filtering a resource based on one of its attributes and specifying a resource.

    For example, since

    /cars/colors returns a list of all colors for all cars (the resource returned is a collection of color objects)

    /cars/colors/red,blue,green would return a list of color objects that are red, blue or green, not a collection of cars.

    To return cars, the path would be

    /cars?color=red,blue,green or /cars/search?color=red,blue,green

  • It is more difficult to read because name/value pairs are not isolated from the rest of the path, which is not name/value pairs.

One last comment. I prefer '/garages/yyy/cars' (always plural) to '/garage/yyy/cars' (perhaps it was a typo in the original answer) because it avoid changing the path between singular and plural. For words with an added 's', it's not so bad, but changing /person/yyy/friends to /people/yyy seems cumbersome.

Doug D
yes, I agree...besides I thing urls path structure should reflect the natural relations between entities, some sort of a map of my resources, like a garage has many cars, a car belongs to a garage and so...and let the filter parameters, cause that's what we are talking about, to que querystring... what do you think?
opensas
+4  A: 

To expand on Peter's answer - you could make Search a first-class resource:

POST    /searches          # create a new search
GET     /searches          # list all searches (admin)
GET     /searches/{id}     # show the results of a previously-run search
DELETE  /searches/{id}     # delete a search (admin)

The Search resource would have fields for color, make model, garaged status, etc and could be specified in XML, JSON, or any other format. Like the Car and Garage resource, you could restrict access to Searches based on authentication. Users who frequently run the same Searches can store them in their profiles so that they don't need to be re-created. The URLs will be short enough that in many cases they can be easily traded via email. These stored Searches can be the basis of custom RSS feeds, and so on.

There are many possibilities for using Searches when you think of them as resources.

The idea is explained in more detail in this Railscast.

Rich Apodaca
doesn't this approach goes against the idea of working with a restless protocol? I mean, persisting a search to a db is sort of having a stateful connection... isn't it?
opensas
It's more like having a stateful service. We're also changing the state of the service every time we add a new Car or Garage. A Search is just another resource that can be used with the full range of HTTP verbs.
Rich Apodaca
Defining URI conventions as part of your API violates a constraint of REST.
Wahnfrieden
How does the above define a URI convention?
Rich Apodaca
That is, how does the above define a convention inconsistent with REST?
Rich Apodaca
REST has nothing to do with pretty URIs or URI nesting etc. If you define URIs as part of your API, it is not REST.
Wahnfrieden
By using URI template your running the risk on not following HATEOAS that differentiates REST from other architecture. Pretty uri are useful for the developer developing the platform not for the developer that consume the resource.
DomreiRoam
+6  A: 

For the searching, go ahead and use a regular old search:

/car-search?color=blue&type=sedan&doors=4

An advantage to regular querystrings is that they are standard and widely understood and that they can be generated from form-get.

pbreitenbach
This is correct. The whole point of query strings is for doing things like search.
Wahnfrieden
+2  A: 

This is not REST. You cannot define URIs for resources inside your API. Resource navigation must be hypertext-driven. It's fine if you want pretty URIs and heavy amounts of coupling, but just do not call it REST, because it directly violates the constraints of RESTful architecture.

See this article by the inventor of REST.

Wahnfrieden
You are correct that it is not REST, it is URL design for a RESTful system. You are also, however incorrect in saying that it violates RESTful architecture. The hypertext constraint of REST is orthogonal to good URL design for a RESTful system; I remember there being a discussion with Roy T. Fielding on the REST list several years ago that I participated in where he stated so explicitly. Said another way it is possible to have hypertext and URL design. URL design for RESTful systems is like indentation in programming; not required but a very good idea (ignoring Python, etc.)
MikeSchinkel
I'm sorry, you're correct. I just got the impression from the OP that he was going to make the clients aware of how to construct URLs - he would make URL "layouts" part of his API. *That* would be a violation of REST.
Wahnfrieden