Steps of designing a REST-ful web service
Bringing together the pieces of the puzzle
In my earlier article, I introduced the concept of REST web services. Before rushing on to how to implement or code a REST web service,
first we have to stop at the design station. I know many developers who are more eager to start coding before spending some time on a proper design. Somehow
they overlook the fact that you do not need Visual Studio to design; a whiteboard is sufficient.
Given a web service requirement, the first step of REST design is to determine the objects we will expose and their respective
representations. An object's representation can be thought of as its set of properties. Representation needs to be designed
carefully, and we will soon see about that.<o:p>
Let us try to build on the classic example of Employee Management. Imagine a web service aiming to expose an employee management
interface. This is a good start because the object to expose is easy to agree upon – employee. Remember, in REST, we expose objects and not
methods.
<o:p>The next step is to nail down the representation of the object – how about a simple XML structure sufficient for our
example:
<o:p>
<Employee id='25648'>
<Name>Peter Scheufele</Name>
<Address>123 Main St. Dallas TX 75226</Address>
<Designation>System Analyst</Designation>
<HireDate>08/20/2007</HireDate>
</Employee>
Here, I need to stress on a few vital points about what representation really means and how it should be designed. To let me do
that, let us examine the concepts of resource and URI.<o:p>
In the era of customer service, the biggest drive for REST web services is that it is supposed to make it easy for the clients. How much
easier does it get for clients if they directly type in the object they want to operate on, in the URL?
Imagine an HR associate
wanting to view employee Peter Scheufele's details – and she typing in
<o:p> http://employeeinfo/v1.4/employee/12098
<o:p>in her browser's address bar! She has to know that 12098 is Peter's Employee Id. Even if she does not know that, how about the
URL:
<o:p> http://employeeinfo/v1.4/employees?name=peter
<o:p> and get back a list of employees whose names have "peter" in them?
<o:p>
Note the words employee and employees in the two URLs above. They are the
resources. Resources are the objects exposed by the REST web service and accessed using the URL itself. In this example, the web service exposes two objects – employee,
representing 1 employee, and employees, representing the collection of employees. Instead of being an URL, these addresses above are, truly
speaking, URI-s. Because they do a little bit more than locate a resource, they actually identify a resource.
That is why these are uniform resource identifiers. <o:p>
The representation of a resource should be thought as the state of a resource. If the representation changes,
it is an UPDATE operation on that resource. The representation is typically returned in response to a GET. We made the representation of
employee object an XML. Must it be XML? No. It can be anything, but XML is hard to beat. Good REST web services also support JSON along with XML,
but let us stick to XML for a while as we clear up the basics. <o:p>
Returning to HTTP verbs, REST sets up a rudimentary guideline: what should a web service do in reaction to the HTTP verbs GET, PUT,
POST and DELETE? Here are the guidelines: <o:p>
- GET: Return (retrieve) the representation of the
resource identified on the URI
<o:p> Example:
<o:p> Client --> Server: Client calls
our web service to get the details of Peter Scheufele:<o:p>
GET http:
Accept: *
<o:p>
<o:p>Server --> Client: Server responds with Peter Scheufele's
representation:<o:p>
HTTP/1.1 200 Ok
Server: Apache-Coyote/1.1
Last-Modified: Thu, 11 Oct 2007 14:45:35 GMT
ETag: 12098
Location: <a href="http://employeeinfo/v1.4/employee/12098">http:
Date: Fri, 12 Oct 2007 12:00:28 GMT
Connection: Close
Content-Type: text/xml; charset=UTF-8
Content-Length: 2324
<Employee id='12098'>
<Name>Peter Scheufele</Name>
<Address>123 Main St. Dallas TX 75226</Address>
<Designation>System Analyst</Designation>
<HireDate>08/20/2007</HireDate>
</Employee>
<o:p>Notes: Note
that the request does not have a HTTP body, but the response contains the Peter
Scheufele's representation as the HTTP body, and it sets some specific HTTP
headers to certain values – these being REST's
best practices.<o:p>
- PUT: Update the representation of the resource identified on the
URI
<o:p>Example:<o:p>
<o:p>Client --> Server: Client calls our web service to update Peter Scheufele's
address:<o:p>
PUT http:
Accept: *
<o:p>Server --> Client: Server updates Peter Scheufele's address and responds with
Peter Scheufele's representation:<o:p>
HTTP/1.1 200 Ok
Server: Apache-Coyote/1.1
Last-Modified: Thu, 11 Oct 2007 14:45:35 GMT
ETag: 12098
Location: <a href="http://employeeinfo/v1.4/employee/12098">http:
Date: Fri, 12 Oct 2007 12:00:28 GMT
Connection: Close
Content-Type: text/xml; charset=UTF-8
Content-Length: 2214
<Employee id='12098'>
<Name>Peter Scheufele</Name>
<Address>6777 Spring Creek Dr. Fairview TX - 56458</Address>
<Designation>System Analyst</Designation>
<HireDate>08/20/2007</HireDate>
</Employee>
<o:p> Notes: Note that the request sends in the
employee representation XML as the HTTP body, but interestingly, it only sends the information that needs to be updated. This is not part of any guideline –
it is up to you, the designer of the web service, to dictate – whether you expect client to send the entire representation XML on update, or just the
elements that need to be updated. A well-architected system will usually use a XSD to validate the incoming XML, so the XML schema needs to be designed such
that it allows incremental update XML-s. Here we have significantly deviated from pure REST discussion, because these deliberations are application specific
design, but I thought these are necessary to highlight the practical aspects of REST.<o:p>
Note that the response carries with it the entire representation of Peter Scheufele, this being a
REST best practice. When a resource's representation is updated, the web service should send the entire representation back to client on successful update.
Again, this is not a "cannot-violate" rule, just recommended.
The other two verbs: POST and DELETE
Now that we have seen GET and PUT in action, it is time to deal with POST. Before diving deep into POST, let us quickly look back at the
definitions of GET and PUT, already stated above once:<o:p>
GET: Return (retrieve) the representation of the resource identified on the URI
PUT: Update the representation of the resource identified on the URI<o:p>
Notice how both these verbs act on the resource's representation? Do you recall the high level objective of REST explained in
the previous article? The whole idea of a few standard verbs acting upon exposed nouns? We see exactly that happening here: GET and PUT
just acted on the resource employee/12098 in a standard, predefined manner: GET retrieved it, PUT updated it.<o:p>
To be more technically correct, GET retrieved the representation, PUT updated the representation. Thus, we now have a
clearer picture of what a representation is: it acts as the alias of the resource – the part that HTTP verbs actually act
upon.<o:p>
Having said that, let us turn to POST. In Rest, POST means create a new resource. Can you visualize such a
request acting upon the representation of some existing resource? How is that possible?<o:p>
Thus, if the client does a POST to http://employeeinfo/v1.4/employee/12098, is that meaningful?
Obviously not – because the new resource to be created is not related to 12098 (Peter Scheufele).<o:p>
What if the POST comes to http://employeeinfo/v1.4/employee? Well – though that is a little bit more meaningful, still we are
having difficulty to enforce the rule – a REST request should act on the representation of the resource identified by the URI.
First of all, the URI here does not identify any particular employee, and there is no representation of just employee.<o:p>
This is exactly where the necessity of a collection resource arises. We did, briefly, mention a collection resource when we
were visualizing an HR employee fiddling with REST.<o:p>
A collection resource has its own representation, and is characterized by the fact that it does not need any specific instance
to be accessed. Let us, for our web service, expose the second object:<o:p>
employees<o:p>
Being just the plural form of employee, employees is a collection resource with the following representation:
<o:p>
<Employees>
<Employee id='12098'>
<Name>Peter Scheufele</Name>
<Address>6777 Spring Creek Dr. Fairview TX - 56458</Address>
<Designation>System Analyst</Designation>
<HireDate>08/20/2007</HireDate>
</Employee>
<Employee id='58585'>
<Name>Tina Sreenivas</Name>
<Address>324 Valley View Rd. Anna TX - 75848</Address>
<Designation>HR Analyst</Designation>
<HireDate>01/10/2005</HireDate>
</Employee>
<!-- Multiple Employee element supported -->
</Employees>
It is not necessary to use the plural as the name of a collection resource – we can call it EmployeesColl or
AllEmployees or whatever. Also, if the individual representation of each employee is too large, it may be impractical and performance
degrading to pack the entire representation of each employee inside employees. In that case, we can define the representation of
employees as follows:<o:p>
<Employees>
<Employee id='12098'>
<a href="http://employeeinfo/v1.4/employee/12098">http:
</Employee>
<Employee id='58585'>
http:
</Employee>
<!-- Multiple Employee element supported -->
</Employees>
<o:p>
This representation simply packs the URI-s of each employee in the collection instead of the full representation of each employee. The
client then would need to issue GET commands for each employee (on their respective URI-s) to get the individual data back.<o:p>
Note that the exposed object employees does not need any specific employee id to be specified – hence it can be thought of as a
global resource. If client does a GET on employees, it will look like:
GET http:
<o:p>The response will contain the entire representation of employees, containing all the employees in it.<o:p>
In REST, you can also define your own query string parameters. You can expose a search functionality using a
combination of GET and the resource employees. For example, you could support query string parameters name and
address. In response, the representation XML that you return might contain only those employees which satisfy the search criterion. For
example:<o:p>
GET http:
<o:p>The above request will return a list of all employees whose name has "george" in it.<o:p>
Suddenly, a whole new set of infinite possibilities are opening up – aren't they? With a clever combination of resources that you
define, their representation that you design, and the query string parameters that you devise – the world is yours to conquer! While a traditional web
service's methods provide a drab dull unilateral dimension of exposing your functionality – REST provides you with an array of interesting
components to permute and combine into a dazzling array of services – all within the framework of standardized HTTP verbs and their standardized
meanings.<o:p>
Getting excited about it? Let us return to POST. You have probably already guessed it – the POST verb, in our example, should be allowed
only on employees resource.<o:p>
Thus, the request:<o:p>
POST http:
<Employee id=''>
<Name>Bruce Evans</Name>
<Address>1 ABC Rd. OK - 85748</Address>
<Designation>Assistant Manager</Designation>
<HireDate>11/12/2007</HireDate>
</Employee>
Is requesting the creation of a new employee – Bruce Evans. And note how that follows the REST guideline - a REST request
should act on the representation of the resource identified by the URI. Are we following this guideline? To the letter! Look how: what is the
resource identified in this URI? Employees. What is its representation? The XML containing all the employee elements. This request acts
on that representation, because it creates another employee node at the end.<o:p>
A few points to note about this POST request: It is not sending in the employee id in the request XML, because the new employee is not
yet created. It has to pass in enough information so that the service can create a new employee. And the response may either return the representation of
employees with only the newly created employee in it, or it may return the representation of the newly created employee. The response
should have the id of the new employee in it. The response should carry the HTTP response code 201 Created (and not 200 Ok).<o:p>
The DELETE verb is used to delete a resource. Now that we have two resources: employee and employees, DELETE can act
on either, but in order to act on employees, it has to specify which employee to delete, which it can do by a query string id.
Thus:<o:p>
DELETE http:
And<o:p>
DELETE http:
Both should be treated equivalently. As designer, you can restrict the delete operation to only one of these – you may choose to say
that DELETE should only be sent to employee resource. That will save you from adding yet another query string parameter id.<o:p>
Output of design
The output of our design so far is what I call an action matrix. It lays down, as a grid, how our web service reacts to different requests - which are combinations of the HTTP verbs, URI-s, query string parameters, etc.<o:p>
Verb | URI | Query String | Request Body | Response Code | Resp Body | Other Remarks |
---|
GET | …/employee/{id} | ignored | ignored | 200 Ok | ERX | 404 Not Found response if id does not exist |
PUT | …/employee/{id} | ignored | ERX | 200 Ok | ERX | Partial ERX OK in input |
POST | …/employee/{id} | ignored | ignored | 405 Not Allowed | error XML | POST forbidden on this URI |
DELETE | …/employee/{id} | ignored | ignored | 200 Ok | empty | 404 Not Found response if id does not exist |
GET | …/employee | ignored | ignored | 400 Bad request | empty | GET must specify employee id in URI |
PUT | …/employee | ignored | ignored | 400 Bad request | empty | PUT must specify employee id in URI |
POST | …/employee | ignored | ERX | 201 Created | ERX | New employee will be created if data is sufficient in request ERX. Else 400 response. |
DELETE | …/employee | ignored | ignored | 400 Bad request | empty | DELETE must specify employee id in URI |
GET | …/employees | name, address, id | ignored | 200 Ok | ESRX | All employees returned if no query string param. Search result returned if query string. |
PUT | …/employees | ignored | ignored | 405 Not Allowed | error Xml | PUT not allowed on employees URI |
POST | …/employees | ignored | ERX | 201 Created | ESRX | New employee will be created if data is sufficient in request ERX. Else 400 response. |
DELETE | …/employees | id | ignored | 200 Ok | empty | 404 response if bad id, 400 if id param missing |
ERX = Employee Representation XML, ESRX = Employees Representation XML.<o:p>
I have not included a column that explains the operation - which would have said "Updates employee data" for the first PUT record - the space constraint of drawing an HTML table is very annoying. Thus, this table defines the behavior of our web service. You can say, this is the REST equivalent of the WSDL - the contract! This is what the designer passes on to developers of both the service and the client. And trying to figure this matrix out for your specific web service need is what this article is about - REST design.<o:p>
REST does not enforce a lot
Now that we have designed a simple REST web service, it is time to tell you that we have used a few concepts that REST does not
necessarily enforce. For example, we created a collection resource because POST to an employee resource does not make sense. But as a web service
designer, you could have said, "I don't care". You can write your web service in such a way that new employee creation must be done by posting to
employee. Thus, consider a request like the following:<o:p>
POST http:
<Employee id=''>
<Name>Bruce Evans</Name>
<Address>1 ABC Rd. OK - 85748</Address>
<Designation>Assistant Manager</Designation>
<HireDate>11/12/2007</HireDate>
</Employee>
That might a valid legitimate request for your web service, and there is nothing overtly wrong with that. It does slightly violate the
REST rule that a REST request should act on the representation of the resource identified by the URI. But this is like bending the
rule a little bit.<o:p>
I have seen a lot of web services bending rules like that – and nobody is sad. As long as it makes sense to clients, as long as it does
not violate the basic tenets of the HTTP verbs: GET is retrieval, PUT is updating, POST is creation and DELETE is deletion, any REST design is OK. The
perfect design, according to me, will, however, try to follow the best practices guidelines and not bend any rules.<o:p>
Thus, the bottom line is your definition of the resources (objects), your construction of the URI-s and the
representation-s, and your final action matrix: which verb applied to which URI along with what query string parameters result in what action – is
all what is REST design is about.<o:p>
Complex actions
So far, the REST web service we have designed can effectively replace a traditional web service with the following web methods:<o:p>
GetEmployeeInfo, SetEmployeeInfo, AddEmployee and DeleteEmployee.
Now, say the traditional web service also has another method –
'TransferEmployee'
How will we implement it in REST? Whenever we face a situation like this, we should ask ourselves: does this action change the
representation of the resource? Well, does it? In this case, answer is: yes it does, only if the representation contains
location. That results in modification of our representation XML – we will add location to it. And we will use the PUT
verb to implement TransferEmployee. Because now TransferEmployee becomes a simple update call – it is nothing but updating
the representation with a new location.<o:p>
Sometimes it becomes a tad difficult to answer the question – does this action change the representation? Because some
operations seem to be such that they don't really appear to change any properties. Let us imagine a web service for lending decisions. If you walk into a
bank asking for an automobile loan, the banker, in most cases submits the approval request to a centrally hosted decision maker which all the bank branches
use. Let us imagine this to be a web service. Let us imagine a traditional web service with the method GetLoanApprovalDecision – this being the web
method that the clients running on the bankers' machines call. How will we model a REST web service to do this very job?<o:p>
Here, the design challenge is to give the right amount of turns to our rubik's cube's sub-cubes – we have resources, URI-s,
representations and query string parameters as our raw materials.<o:p>
Our web service might have customer as a resource – but will that help us to model
GetLoanApprovalDecision? Not much. How about having the resource loan? Will that help us create a representation which we
can refer when we ask – does GetLoanApprovalDecision change this representation? Or, will application be a better resource
to expose?<o:p>
There are no rights or wrongs – only betters and worses. I would go for application resource – with the
representation XML containing the personal data of the applicant and a section called Status. The request will leave it blank, and the response will
fill it up with "Approved" or "Declined" or "Referred to underwriter". I would use POST to create a new application. The creation request will
return an application id. After creation, I would support a GET on a specific application resource to get the status. That would be my equivalent of
GetLoanApprovalDecision – notice that I have split it up into 2 REST calls – POST and GET. The POST creates an application resource, the GET
retrieves the status. I could also have a query string parameter view that will be supported by GET, with possible values
all, applicant and status. While the response to "all" will return the entire
representation XML, "applicant" will return only the sub-node that contains the applicant data, and "status" will return the loan approval/ decline decision
part. Thus, here is what my REST equivalent of GetLoanApprovalDecisionlooks like:<o:p>
<o:p>
Call 1:
POST http:
<application>
…
</application>
Response to call 1 returns the representation XML of loan application resource containing the application id. Say the id
generated is 546. In that case, call 2 will be:
Call 2:
GET http:
Conclusion
In the next and last part, we will touch upon implementation. We will create a REST web service and the corresponding REST client – in
.NET 2.0. Yes, you heard right - .NET 2.0. Microsoft released the .NET 2.0 platform fully geared for SOAP, and when they sensed the growing urgency for REST support, they begun spraying in REST support in 3.0 - and the promised 3.5 version is nothing but REST! But in spite of lack of support, you can still develop a REST web service in .NET 2.0. I will try to elaborate on that in the next article. That will conclude this series on REST web services.<o:p>