O'Reilly logo

RESTful Web Services by Sam Ruby, Leonard Richardson

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Name the Resources

I’ve decided on five types of resources (see Example 5-1). Now they need names. Resources are named with URIs, so let’s pick some. Remember, in a resource-oriented service the URI contains all the scoping information. Our URIs need to answer questions like: “Why should the server operate on this map instead of that map?” and “Why should the server operate on this place instead of that place?”

I’ll root my web service at http://maps.example.com/. For brevity’s sake I sometimes use relative URIs in this chapter and the next; understand that they’re relative to http://maps.example.com/. If I say /Earth/political, what I mean is http://maps.example.com/Earth/political.

Now let’s consider the resources. The most basic resource is the list of planets. It makes sense to put this at the root URI, http://maps.example.com/. Since the list of planets encompasses the entire service, there’s no scoping information at all for this resource (unless you count the service version as scoping information).

For the other resources I’d like to pick URIs that organize the scoping information in a natural way. There are three basic rules for URI design, born of collective experience:

  1. Use path variables to encode hierarchy: /parent/child

  2. Put punctuation characters in path variables to avoid implying hierarchy where none exists: /parent/child1;child2

  3. Use query variables to imply inputs into an algorithm, for example: /search?q=jellyfish&start=20

Encode Hierarchy into Path Variables

Let’s make URIs for the second class of resource: planets and places on planets. There’s one piece of scoping information here: what planet are we looking at? (Earth? Venus? Ganymede?) This scoping information fits naturally into a hierarchy: the list of planets is at the top, and underneath it is every particular planet. Here are the URIs to some of my planets. I show hierarchy by using the slash character to separate pieces of scoping information.

  • http://maps.example.com/Venus

  • http://maps.example.com/Earth

  • http://maps.example.com/Mars

To identify geographical places by name I’ll just extend the hierarchy to the right. You’ll know you’ve got a good URI design when it’s easy to extend hierarchies by tacking on additional path variables. Here are some URIs to various places on planets:

  • http://maps.example.com/Venus

  • http://maps.example.com/Venus/Cleopatra

  • http://maps.example.com/Earth/France/Paris

  • http://maps.example.com/Earth/Paris,%20France

  • http://maps.example.com/Earth/Little%20Rock,AR

  • http://maps.example.com/Earth/USA/Mount%20Rushmore

  • http://maps.example.com/Earth/1005%20Gravenstein%20Highway%20North,%20Sebastopol,%20CA%2095472

We’re now deep into web service territory. Sending a GET to one of these URIs invokes a remote operation that takes a variable number of arguments, and can locate a place on a planet to any desired degree of precision. But the URIs themselves look like normal web site URIs you can bookmark, cache, put on billboards, and pass to other services as input—because that’s what they are. Path variables are the best way to organize scoping information that can be arranged hierarchically. The same structure you see in a filesystem, or on a static web site, can correspond to an arbitrarily long list of path variables.

No Hierarchy? Use Commas or Semicolons

The next resources I need to name are geographic points on the globe, represented by latitude and longitude. Latitude and longitude are tied together, so a hierarchy isn’t appropriate. A URI like /Earth/24.9195/17.821 doesn’t make sense. The slash makes it look like longitude is a subordinate concept to latitude, the way /Earth/Chicago signals that Chicago is part of Earth.

Instead of using the slash to put two pieces of scoping information into a hierarchy, I recommend combining them on the same level of a hierarchy with a punctuation character: usually the semicolon or the comma. I’m going to use a comma to separate latitude and longitude. This yields URIs like the following:

  • http://maps.example.com/Earth/24.9195,17.821

  • http://maps.example.com/Venus/3,-80

Latitude and longitude can also be used as scoping information to uniquely identify a named place. A human would probably identify Mount Rushmore as /Earth/USA/Mount%20Rushmore or as /v1/Earth/USA/SD/Mount%20Rushmore, but /v1/Earth/43.9,-103.46/Mount%20Rushmore would be more precise.

From a URI design perspective, the interesting thing here is that I’m stuffing two pieces of scoping information into one path variable. The first path variable denotes a planet, and the second one denotes both latitude and longitude. This kind of URI may look a little strange, because not many web sites or services use them right now, but they’re catching on.

I recommend using commas when the order of the scoping information is important, and semicolons when the order doesn’t matter. In this case the order matters: if you switch latitude and longitude, you get a different point on the planet. So I used commas to separate the two numbers. It doesn’t hurt that people already use commas in written language to separate latitude and longitude: URIs should use our existing conventions when possible.

In another case the order might not matter. Consider a web service that lets you mix colors of paint to get the shade you want. If you’re mixing red and blue paint, it doesn’t matter whether you pour the red into the blue or the blue into the red: you get purple either way. So the URI /color-blends/red;blue identifies the same resource as /color-blends/blue;red. I think the semicolon is better than the comma here, because the order doesn’t matter. This is just a typographical convention, but it helps a human being make sense of your web service URIs. The use of the semicolon feeds into an obscure idea called matrix URIs, a way of defining key-value pairs in URIs without using query variables. Some newer standards, like WADL, offer support for matrix URIs. They’re especially useful if you ever need to put key-value pairs in the middle of a hierarchy.


URIs can become very long, especially when there’s no limit to how deep you can nest the path variables. My web service might let clients name a place using a lot of explicit scoping information: /Earth/North%20America/USA/California/Northern%20California/San%20Francisco%20Bay%20Area/Sebastopol/...

The HTTP standard doesn’t impose any restrictions on URI length, but real web servers and clients do. For instance, Microsoft Internet Explorer can’t handle URIs longer than 2,083 characters, and Apache won’t respond to requests for URIs longer than 8 KBs. If some of your resources are only addressable given a great deal of scoping information, you may have to accept some of it in HTTP headers, or use overloaded POST and put scoping information in the entity-body.

Map URIs

Now that I’ve designed the URI to a geographic point on a planet, what about the corresponding point on a road map or satellite map? After all, the main point of this service is to serve maps.

Earlier I said I’d expose a resource for every point on a map. For simplicity’s sake, I’m not exposing maps of named places, only points of latitude and longitude. In addition to a set of coordinates or the name of a place, I need the name of the planet and the type of map (satellite map, road map, or whatever). Here are some URIs to maps of planets, places, and points:

  • http://maps.example.com/radar/Venus

  • http://maps.example.com/radar/Venus/65.9,7.00

  • http://maps.example.com/geologic/Earth/43.9,-103.46


A URI like /satellite/Earth/41,-112 says nothing about how detailed the map should be. I’m going to extend the first path variable so that it doesn’t just specify the type of map: it can also specify the scale. I’ll expose a very small-scale map at /satellite.10/Earth, a very large-scale map at /satellite.1/Earth, and maps of other scales in between. I’ll choose a sensible default scale: probably a large scale like 2. Here are some possible URIs for the same map at different scales:

  • /satellite.10/Earth/41,-112: 1:24,000; 2,000 feet to the inch. A map for hiking or prospecting. Centered on 41°N 112°W on Earth, this map would show the banks of Utah’s Great Salt Lake.

  • /satellite.5/Earth/41,-112: 1:250,000; 4 miles to the inch. The scale of a highway map. Centered on 41°N 112°W, this map would show the northern suburbs of Salt Lake City.

  • /satellite.1/Earth/41,-112: 1:51,969,000; 820 miles to an inch. (That’s 820 miles/inch at the equator. At this scale, the curvature of the earth distorts the scale of a 2D map.) The scale of a world map. Centered on 41°N 112°W, this map would show much of Utah and surrounding states.

The scale affects not only the natural size of the map in pixels, but which features are shown. A small town would be represented in fair detail on a map at scale 10, but would only be a point at scale 5 if it showed up at all.

How did I decide that scale 1 would be a large-scale map, and scale 10 would be a small-scale map? Why not the reverse? I used a common technique for URI design. I exaggerated the decision I was making, figured out how the generalized situation should work, and then scaled my decision back down.

Maps can always get more detailed,[21] but there’s a limit how small they can get. If I decide to acquire some new data for my map service, I’d never buy a map that shows the world in less detail than the world map at scale 1. There’d be no point. However, it’s quite possible that I’ll find maps that are more detailed than the one at scale 10. When I find those maps, I can make them available through my service and assign them scales of 11, 12, and so on. If I’d assigned the most detailed map a scale of 1, I’d have to assign scales of 0, –1, and so on to any new maps. The URIs would look strange. This means larger numbers make good URIs for more detailed maps. I may never actually get those more detailed maps, but thinking about them revealed a truth about my URI design.

Algorithmic Resource? Use Query Variables

Most web applications don’t store much state in path variables: they use query variables instead. You may have seen URIs like this:

  • http://www.example.com/colorpair?color1=red&color2=blue

  • http://www.example.com/articles?start=20061201&end=20071201

  • http://www.example.com/weblog?post=My-Opinion-About-Taxes

Those URIs would look better without the query variables:

  • http://www.example.com/colorpair/red;blue

  • http://www.example.com/articles/20061201-20071201

  • http://www.example.com/weblog/My-Opinion-About-Taxes

Sometimes, though, query variables are appropriate. Here’s a Google search URI: http://www.google.com/search?q=jellyfish. If the Google web application used path variables, its URIs would look more like directories and less like the result of running an algorithm: http://www.google.com/search/jellyfish.

Both of those URIs would be legitimate resource-oriented names for the resource “a directory of web pages about jellyfish.” The second one doesn’t look quite right, though, because of how we’re socialized to look at URIs. Path variables look like you’re traversing a hierarchy, and query variables look like you’re passing arguments into an algorithm. “Search” sounds like an algorithm. For example, http://www.google.com/directory/jellyfish" might work better than /search/jellyfish.

This perception of query variables is reinforced whenever we use the Web. When you fill out an HTML form in a web browser, the data you input is turned into query variables. There’s no way to type “jellyfish” into a form and then be sent to http://www.google.com/search/jellyfish. The destination of an HTML form is hard-coded to http://www.google.com/search/, and when you fill out that form you end up at http://www.google.com/search?q=jellyfish. Your browser knows how to tack query variables onto a base URI. It doesn’t know how to substitute variables into a generic URI like http://www.google.com/search/{q}.

Because of this precedent, a lot of REST-RPC hybrid services use query variables when it would be more idiomatic to use path variables. Even when a hybrid service happens to expose resources RESTfully, the resources have URIs that make them look like function calls: URIs such as http://api.flickr.com/services/rest/?method=flickr.photos.search&tags=penguin. Compare that URI to the corresponding URI on the human-usable Flickr site: http://flickr.com/photos/tags/penguin.

I’ve managed to avoid query variables so far: every planet, every point on a planet, and every corresponding map is addressable without them. I don’t really like the way query variables look in a URI, and including them in a URI is a good way to make sure that URI gets ignored by tools like proxies, caches, and web crawlers. Think back to the Google Web Accelerator I mentioned in Why safety and idempotence matter” in Split the Data Set into Resources. It never pre-fetches a URI that includes a query variable, because that’s the kind of URI exposed by poorly-designed web applications that abuse HTTP GET. My service won’t abuse GET, of course, but outside applications have no way of knowing that.

But I’ve got one more type of resource to represent—lists of search results—and I’m out of tricks. It doesn’t make sense to keep going down the hierarchy of place, and I can’t keep piling on punctuation just to avoid the impression that my service is running an algorithm. Besides, this last type of resource is the result of running an algorithm. My search algorithm finds places that match map-specific criteria, just as a search engine finds web sites that match the client’s keywords. Query variables are perfectly appropriate for naming algorithmic resources.

The search interface for places can get as complex as I need it to be. I could expose a name query variable for place names and pollutant for sites of high pollution and cuisine for restaurants and all sorts of other query variables. But let’s imagine I’ve got the technology to make it simple. The only query variable I’ll add is show, which lets the client specify in natural language what feature(s) they’re searching for. The server will parse the client’s values for show and figure out what places should be in the list of search results.

In Split the Data Set into Resources” earlier in this chapter, I gave a whole lot of sample search resources: “places on Earth called Springfield,” and so on. Here’s how a client might use show to construct URIs for some of those resources.

  • http://maps.example.com/Earth?show=Springfield

  • http://maps.example.com/Mars?show=craters+bigger+than+1km

  • http://maps.example.com/Earth/Indonesia?show=oil+tankers&show=container+ships

  • http://maps.example.com/Earth/USA/Mount%20Rushmore?show=diners

  • http://maps.example.com/Earth/24.9195,17.821?show=arsenic

Note that all of these URIs are searching the planet, not any particular map.

URI Recap

That’s a lot of details. After all, this is the first place where my fantasy resources come into contact with the real world of HTTP. Even so, my service only supports three basic kinds of URI. To recap, here they are:

  • The list of planets: /.

  • A planet or a place on a planet: /{planet}/[{scoping-information}/][{place-name}]: The value of the optional variable {scoping-information} will be a hierarchy of place names like /USA/New%20England/Maine/ or it will be a latitude/longitude pair. The value of the optional variable {name} will be the name of the place.

    This type of URI can have values for show tacked onto its query string, to search for places near the given place.

  • A map of a planet, or a point on a map: /{map-type}{scale}/{planet}/[{scoping-information}]. The value of the optional variable {scoping-information} will always be a latitude/longitude pair. The value of the optional variable {scale} will be a dot and a number.

[21] Up to a point, anyway. See On Exactitude in Science by Jorge Luis Borges.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required