Off to the races: Building a RESTful API
In this section, you'll build a RESTful API for a Web service that leverages the functionality of an existing database-backed application.
Imagine an online application that manages races in which contestants run various distances (such as the Chicago Marathon). The application manages races (or events) and the runners associated with them. And it reports a particular runner's time (how long it took to run the race) and rank (what place the runner finished in). The race-management company, Acme Racing, wants you to build a RESTful Web service that enables sponsors to create new races and racers for a particular race, and that can provide official results for a particular race.
Acme Racing already has a legacy fat-client application that supports similar requirements and leverages a simple database along with a domain model. Thus, the job of exposing this functionality is all that's left to do. Remember that the beauty of REST is its implicit loose coupling with an underlying application. Accordingly, your job, at the moment, isn't to worry about the data model or the technology associated with it — it's to construct a RESTful API that supports the company's requirements.
Acme Races would like sponsors to be able to:
- View the details of existing races
- Create new races
- Update existing races
- Delete races
Because REST boils down to named resources, the API becomes a series of URI patterns, and the behavior associated with a resource is invoked via standard HTTP commands.
As you can see, the client's requirements map nicely to CRUD. And as you know from Table 1, REST supports CRUD via the HTTP
DELETE requests, respectively. Accordingly, a base RESTful URI that supports these requirements could be http://racing.acme.com/race. Note that in this case, race is the resource clients would work with.
Invoking this URI with an HTTP
GET would return a list of races. (Don't worry about the format of the response just yet.) To add a new race, you would invoke the same URI with an HTTP
POST containing the appropriate information (for instance, an XML document containing required race information, such as name, date, and distance).
For updating and deleting existing races, you would need to act on a particular
instance of a race. Accordingly, individual races can be addressed with a URI of
http://racing.acme.com/race/race_id. In this case,
race_id represents a placeholder for any race identifier (such as 1 or 600meter). Consequently, viewing an existing race instance would be an HTTP
GET to that URI; updating or deleting a race would be a
DELETE request, respectively.
Acme Racing also wishes to expose data regarding runners associated with a race. They'd like their service to support:
- Obtaining all runners for a particular race. This data should also include run times and ranks for a race that's already completed.
- Creating one or more runners for a particular race.
- Updating a runner's information (such as age) for a particular race.
- Deleting a runner for a particular race.
Acme would also like the service to let users view individual data for a particular runner in a particular race.
Just as with races, applying RESTful URIs to runners associated with a race is a logical exercise. Viewing all runners for a particular race, for example, would be implemented via a
GET request to http://racing.acme.com/race/race_id/runner.
Obtaining individual data for a runner in a race would be addressed as http://racing.acme.com/race/race_id/runner/runner_id.
Just like race_id, runner_id is a placeholder for the logical implementation of IDs, which could be numbers, names, alphanumeric combinations, and so on.
Adding runners to a race would be a
POST request to http://racing.acme.com/race/race_id/runner. Updating or deleting particular runners would be, respectively,
DELETE requests to http://racing.acme.com/race/race_id/runner/runner_id.
Thus, these URIs (each supporting some or all of the four standard HTTP requests) capture Acme Racing's requirements:
Remember, a particular URI can map to more than one HTTP verb (for instance, applying an
GET to /race returns data; applying a
POST with appropriate data creates data on the server). Accordingly, some HTTP commands wouldn't be implemented. For example, /race wouldn't support the
DELETE command (Acme Racing wouldn't want to delete all races); /race/race_id could support the
DELETE command because removing a particular instance of a race is a business requirement.