477

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 guidance 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?

Luuklag
  • 3,897
  • 11
  • 38
  • 57
Parand
  • 102,950
  • 48
  • 151
  • 186
  • 17
    Although it looks better in English, mixing `/cars` and `/car` is not semantical and therefore a bad idea. Always use the plural when there is more than one item under that category. – Zaz Sep 17 '10 at 16:58
  • 5
    These are bad answers. Search should use query strings. Query strings are 100% RESTful when used properly (ie, for search). – pbreitenbach Dec 16 '10 at 08:47
  • 2
    check out http://doriantaylor.com/policy/http-url-path-parameter-syntax – Anatoly G May 17 '11 at 21:58

12 Answers12

488

For the searching, use querystrings. This is perfectly RESTful:

/cars?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.

GWed
  • 15,167
  • 5
  • 62
  • 99
pbreitenbach
  • 11,261
  • 3
  • 33
  • 24
  • 48
    This is correct. The whole point of query strings is for doing things like search. – aehlke Jul 20 '09 at 19:35
  • 24
    Indeed this is correct as, per [RFC3986](http://tools.ietf.org/html/rfc3986#section-3.4), the path _and_ querystring identify the resource. What's more, proper naming would simply be `/cars?color=whatever`. – Lloeki Jun 01 '12 at 12:23
  • 40
    What about cases where you want comparators (>, <, <=, >=)? /cars?rating<=3? – Jesse Apr 26 '13 at 22:52
  • @Jesse Perhaps send the operator as a parameter? Like `cars?rating=3&operator=%3C%3D` – Brian Ortiz Sep 17 '13 at 18:42
  • 3
    What if you want to access resources nested beneath the query string? E.g. `/cars?color=blue&type=sedan&doors=4/engines` won't work – Abe Voelker Oct 02 '13 at 14:45
  • @Jesse (Just a suggestion) go to a site like amazon or cars.com and run fiddler and do some searches. You will be able to see quite a bit on how they handled and overcame these scenarios. – Doug Chamberlain Jan 08 '14 at 13:28
  • 10
    @mjs `/cars?param=value` is for simple **filtering** on the car list and `/cars/search?param=value` is for creating a **search** (with ou without persistance) where the result may contains search scoring, categorisation, etc. You can also create/delete a named search like `/cars/search/mysearch`. Look at that: http://stackoverflow.com/a/18933902/1480391 – Yves M. Jan 30 '14 at 14:32
  • 4
    @YvesM. I was puzzled for a moment but my previous comment referred to the original answer, before it was edited: http://stackoverflow.com/revisions/1081720/1. I agree with you that if a search is distinct thing, then it makes sense for `search` to appear in the name of the resource. I don't think this is what the original question was asking though. – mjs Jan 30 '14 at 16:17
  • I agree with @mjs - SEARCH is an action word. The URL must reflect the organization of resources. You can do many things with a car, or its collection, one of which is search. Therefore, the API should reflect this clearly, and push all variability (many things you can do with car/cars) behind ?. When the search happens without any pre-knowledge of the resource type, then search could becomes the part of API itself. – Kingz Mar 07 '14 at 16:43
  • So what is it about this answer that makes it RESTful? – Dave Van den Eynde Jun 13 '14 at 12:26
  • @pbreitenbach How can I add filter content with spaces like( 'this should be filtered') in REST Url? – saravanakumar Jun 23 '14 at 09:39
  • @saravanakumar Just URL encode the filter. Eg: `/cars?filter=this%20should%20be%20filtered` – Richt222 Sep 26 '14 at 21:24
  • RESTful does not recommend using verbs in URL's /cars/search is not restful. The right way to filter/search/paginate your API's is through Query Parameters. However there might be cases when you have to break the norm. For example, if you are searching across multiple resources, then you have to use something like /search?q=. – java_geek Oct 06 '14 at 06:20
  • 1
    Read through http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/ to understand the best practices for REST API's – java_geek Oct 06 '14 at 06:22
  • @java_geek would it make sense to treat multiple resources as a separate union of resources (ex. /carsandboats?horsepower={value:400,operator:">"} – nclu Aug 06 '15 at 22:14
  • Actually the word Search is just one of the English words that is the same in both Verb and Noun form. It is both. So if nouns are allowed, then the word search is allowed, but one should take extra care to treat it as a noun and not a verb in their rest API. – Rhyous May 02 '16 at 15:17
  • What if I want to search a product using id and barcode? `product/id`, `product/barcode` ? same url pattern but different value for different column – Asif Mushtaq Nov 08 '18 at 17:37
  • any recommendation on how to name it if i have to a post with the parameters being passed in the request body? if i already have POST /cars to create a car and if i do POST /cars would be a duplicate. – user2347528 Jan 16 '20 at 16:20
  • IIRC it's not safe to put sensitive data in the URL (and query params?) because if a browser requests the endpoint it can be intercepted by javascripts? eg. email=abc@example.com would leak PII to other javascripts... – Mike Graf Mar 10 '21 at 22:05
145

The RESTful pretty URL design is about displaying a resource based on a structure (directory-like structure, date: articles/2005/5/13, object and it's attributes,..), the slash / indicates hierarchical structure, use the -id instead.

Hierarchical structure

I would personaly prefer:

/garage-id/cars/car-id
/cars/car-id   #for cars not in garages

If a user removes the /car-id part, it brings the cars preview - intuitive. User exactly knows where in the tree he is, what is he looking at. He knows from the first look, that garages and cars are in relation. /car-id also denotes that it belongs together unlike /car/id.

Searching

The searchquery is OK as it is, there is only your preference, what should be taken into account. The funny part comes when joining searches (see below).

/cars?color=blue;type=sedan   #most prefered by me
/cars;color-blue+doors-4+type-sedan   #looks good when using car-id
/cars?color=blue&doors=4&type=sedan   #also possible, but & blends in with text

Or basically anything what isn't a slash as explained above.
The formula: /cars[?;]color[=-:]blue[,;+&], though I wouldn't use the & sign as it is unrecognizable from the text at first glance if that's your thing.

** Did you know that passing JSON object in URI is RESTful? **

Lists of options

/cars?color=black,blue,red;doors=3,5;type=sedan   #most prefered by me
/cars?color:black:blue:red;doors:3:5;type:sedan
/cars?color(black,blue,red);doors(3,5);type(sedan)   #does not look bad at all
/cars?color:(black,blue,red);doors:(3,5);type:sedan   #little difference

possible features?

Negate search strings (!)
To search any cars, but not black and red:
?color=!black,!red
color:(!black,!red)

Joined searches
Search red or blue or black cars with 3 doors in garages id 1..20 or 101..103 or 999 but not 5 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
You can then construct more complex search queries. (Look at CSS3 attribute matching for the idea of matching substrings. E.g. searching users containing "bar" user*=bar.)

Conclusion

Anyway, this might be the most important part for you, because you can do it however you like after all, just keep in mind that RESTful URI represents a structure which is easily understood e.g. directory-like /directory/file, /collection/node/item, dates /articles/{year}/{month}/{day}.. And when you omit any of last segments, you immediately know what you get.

So.., all these characters are allowed unencoded:

  • unreserved: a-zA-Z0-9_.-~
    Typically allowed both encoded and not, both uses are then equivalent.
  • special characters: $-_.+!*'(),
  • reserved: ;/?:@=&
    May be used unencoded for the purpose they represent, otherwise they must be encoded.
  • unsafe: <>"#%{}|^~[]`
    Why unsafe and why should rather be encoded: RFC 1738 see 2.2

Also see RFC 1738#page-20 for more character classes.

RFC 3986 see 2.2
Despite of what I previously said, here is a common distinction of delimeters, meaning that some "are" more important than others.

  • generic delimeters: :/?#[]@
  • sub-delimeters: !$&'()*+,;=

More reading:
Hierarchy: see 2.3, see 1.2.3
url path parameter syntax
CSS3 attribute matching
IBM: RESTful Web services - The basics
Note: RFC 1738 was updated by RFC 3986

Qwerty
  • 29,062
  • 22
  • 108
  • 136
  • 3
    I don't believe I haven't thought of using JSON in the query string. It's the answer to a problem I was facing - complex search structure without using `POST`. Also, other ideas you gave in your answer are also highly appreciable. Thanks very much! – gustavohenke Nov 28 '13 at 20:07
  • 6
    @Qwerty: great post! I was wondering: the only reason for using `;` as opposed to `&` is readability? Because if so, I think I'd actually prefer the `&` as it's the more common delimiter...right? :) Thanks! – Adam Apr 16 '15 at 01:31
  • 3
    @Flo Yes exactly :), but keep in mind that `&` as a delimiter is known only to developers. Parents, grandparents and the non-it educated population accepts delimiters as used in common written text. – Qwerty Apr 17 '15 at 10:45
  • 28
    Why make up a nonstandard scheme when query strings are well understood and standard? – pbreitenbach Aug 08 '15 at 00:39
  • @pbreitenbach Because that's the whole point of having **RESTful**-like design. Of course we could have `index.php?category=garages/cars&garageid[]=20&garageid[]=21&garagerange[]=5-10&‌​carcolor[]=blue&carcolor[]=red` or we could have `/garages[5-10,20,21]/cars[color=red,blue]`. – Qwerty Aug 08 '15 at 10:37
  • 3
    @Qwerty nothing stopping you from /search?cars=red,blue,green&garages=1,2,3 Or if you use a form: /search?cars=red&cars=blue&garages=1&garages=2 – pbreitenbach Aug 10 '15 at 18:39
  • @pbreitenbach Since he is already using these _"Pretty URLs"_, why combine two schemes? – Qwerty Aug 11 '15 at 11:30
  • the '.' shows up in both reserved and unreserved list above. so which one is it? – duraid Nov 21 '18 at 15:47
  • @duraid Nice catch. That's because each of the characters _**may**_ be reserved within a given scheme or even within a certain part of a scheme. URL is not defined only for http and http itself has several parts. The dot `.` is not reserved in a _query_ part and may be used both un/encoded, for example `?value=1.4`, but encoding it would change the semantics of the url in the _host_ part, such as `sub.example.com`. – Qwerty Nov 21 '18 at 16:41
38

Although having 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.

  • 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

  • Parameters in the path are 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 avoids changing the path between singular and plural. For words with an added 's', the change is not so bad, but changing /person/yyy/friends to /people/yyy seems cumbersome.

Doug Domeny
  • 4,410
  • 2
  • 33
  • 49
  • 2
    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 May 30 '09 at 17:50
36

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
  • 28,316
  • 16
  • 103
  • 129
  • 6
    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 May 30 '09 at 17:48
  • 5
    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 May 31 '09 at 14:46
  • 1
    Defining URI conventions as part of your API violates a constraint of REST. – aehlke Jul 20 '09 at 19:34
  • 2
    How does the above define a URI convention? – Rich Apodaca Jul 21 '09 at 01:59
  • That is, how does the above define a convention inconsistent with REST? – Rich Apodaca Jul 21 '09 at 02:00
  • 4
    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. – aehlke Jul 29 '09 at 20:36
  • 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 Oct 27 '10 at 09:05
  • 3
    I've argued this one before. This is no way stateful, but it is a terrible thing. The 'delete' of the search is not perfectly clear, here you are saying it deletes this search entity, but I would want to use it delete the results I found through that search. Please do not add 'searches' as a resource. – thecoshman Jun 18 '14 at 09:08
11

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
  • 17,211
  • 6
  • 50
  • 75
8

I use two approaches to implement searches.

1) Simplest case, to query associated elements, and for navigation.

    /cars?q.garage.id.eq=1

This means, query cars that have garage ID equal to 1.

It is also possible to create more complex searches:

    /cars?q.garage.street.eq=FirstStreet&q.color.ne=red&offset=300&max=100

Cars in all garages in FirstStreet that are not red (3rd page, 100 elements per page).

2) Complex queries are considered as regular resources that are created and can be recovered.

    POST /searches  => Create
    GET  /searches/1  => Recover search
    GET  /searches/1?offset=300&max=100  => pagination in search

The POST body for search creation is as follows:

    {  
       "$class":"test.Car",
       "$q":{
          "$eq" : { "color" : "red" },
          "garage" : {
             "$ne" : { "street" : "FirstStreet" }
          }
       }
    }

It is based in Grails (criteria DSL): http://grails.org/doc/2.4.3/ref/Domain%20Classes/createCriteria.html

user2108278
  • 391
  • 5
  • 17
4

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.

aehlke
  • 15,225
  • 5
  • 36
  • 45
  • 30
    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 Aug 15 '10 at 16:41
  • 2
    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. – aehlke Aug 16 '10 at 08:40
  • @aehlke, you should update your answer to match your comment. – James McMahon Sep 11 '12 at 20:01
  • @James McMahon, I'm not very active in this site anymore, you're welcome to edit it. – aehlke Sep 12 '12 at 19:53
  • -1 for the very clear explanation already provided by @MikeSchinkel – Chris Marisic Sep 26 '13 at 13:28
  • 1
    It is level 2 [Richardson maturity model](https://martinfowler.com/articles/images/richardsonMaturityModel/overview.png) compliant. You are referring to level 3. Just accept REST as something progressively adoptable. – Jules Sam. Randolph Feb 05 '17 at 16:46
  • 1
    @Jules Randolph - apologies, my answer was written only months after the Richardson maturity model was first coined and before Martin Fowler and other authors popularized it :) Indeed, it's an instructive model to follow. Feel free to edit the answer. – aehlke Feb 07 '17 at 04:09
4

In addition i would also suggest:

/cars/search/all{?color,model,year}
/cars/search/by-parameters{?color,model,year}
/cars/search/by-vendor{?vendor}

Here, Search is considered as a child resource of Cars resource.

aux
  • 1,589
  • 12
  • 20
3

There are a lot of good options for your case here. Still you should considering using the POST body.

The query string is perfect for your example, but if you have something more complicated, e.g. an arbitrary long list of items or boolean conditionals, you might want to define the post as a document, that the client sends over POST.

This allows a more flexible description of the search, as well as avoids the Server URL length limit.

estani
  • 24,254
  • 2
  • 93
  • 76
2

RESTful does not recommend using verbs in URL's /cars/search is not restful. The right way to filter/search/paginate your API's is through Query Parameters. However there might be cases when you have to break the norm. For example, if you are searching across multiple resources, then you have to use something like /search?q=query

You can go through http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/ to understand the best practices for designing RESTful API's

java_geek
  • 17,585
  • 30
  • 91
  • 113
1

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

-3

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://web.archive.org/web/20070815111413/http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful

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

Lie Ryan
  • 62,238
  • 13
  • 100
  • 144
Justin Bozonier
  • 7,584
  • 9
  • 44
  • 46
  • 1
    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/4 That doesn't seem right. – Parand Oct 16 '08 at 05:17
  • 2
    Commas in the URL don't feel right to me, but still valid rest. I think it is just a paradigm shift. – Justin Bozonier Oct 16 '08 at 05:43
  • 21
    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 May 07 '09 at 12:18
  • 1
    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 Oct 11 '10 at 14:53
  • 62
    This a poor answer. In fact, the proper way to implement search is with query strings. Query strings are not evil in the slightest when used properly. The article cited is not referring to search. The examples provided are clearly tortured and would not hold up well with more parameters. – pbreitenbach Dec 16 '10 at 08:39
  • 4
    querystrings were made primarily to resolve the problem of querying a resource, even with multiple parameters. Perverting the URI to enable a "RESTful" API seems dangerous and short sighted - especially since you'd have to write your own complex mappings just to handle the various permutations of parameters on the URI. Better yet, use the already-existing notion of using semicolons in your URIs: http://doriantaylor.com/policy/http-url-path-parameter-syntax – Anatoly G May 17 '11 at 21:58
  • I agree with everything in the 1st box except for `/cars/colors/red,blue,green`. The last part should be a query string. – DanMan May 24 '14 at 15:15