BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Three Levels of the REST Maturity Model

Three Levels of the REST Maturity Model

This item in japanese

Bookmarks

In his new article, Martin Fowler is using the 3-level model of restful maturity that was developed by Leonard Richardson to explain web-style systems. Throughout his explanation Fowler is using an example of a service for booking a doctors appointment.

According to Fowler, the starting point for the maturity model is to use HTTP purely as a transport system for remote interactions. In this case there is a single service - appointment service, which is using a single method call (POST in his example) and an XML input/output to communicate specific requests and replies.

Finding available doctor’s appointments, in this case, will require a request:

POST /appointmentService HTTP/1.1

<openSlotRequest date = "2010-01-04" doctor = "mjones"/>

Which will return a reply:

HTTP/1.1 200 OK

<openSlotList>
   <slot start = "1400" end = "1450">
      <doctor id = "mjones"/>
   </slot>
   <slot start = "1600" end = "1650">
      <doctor id = "mjones"/>
   </slot>
</openSlotList>

And booking the slot will require the following request:

POST /appointmentService HTTP/1.1

<appointmentRequest>
   <slot doctor = "mjones" start = "1400" end = "1450"/>
   <patient id = "jsmith"/>
</appointmentRequest>

With a reply looking like this:

HTTP/1.1 200 OK

<appointment>
   <slot doctor = "mjones" start = "1400" end = "1450"/>
   <patient id = "jsmith"/>
</appointment>

The transition to REST (Level 1) starts with resources. Rather than making all our requests to a singular service endpoint, in the case of REST we deal with individual resources. In the earlier example those are doctors and appointment slots.

Working with resources allows to partition requests. Every resource supports a certain set of functionality. It also allows to simplify requests - referencing a certain resource makes some of the request information implicit. According to Fowler:

To an object guy like me this is like the notion of object identity. Rather than calling some function in the ether and passing arguments, we call a method on one particular object providing arguments for the other information.

In this case an initial query will be to a given doctor resource (the doctor’s name is not in the request anymore, but in the resource definition):

POST POST /doctors/mjones HTTP/1.1

<openSlotRequest date = "2010-01-04"/>

The reply now returns each slot as a directly addressable resource:

HTTP/1.1 200 OK

<openSlotList>
   <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
   <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

The slot ID can now be used for posting booking request:

POST /slots/1234 HTTP/1.1

<appointmentRequest>
   <patient id = "jsmith"/>
</appointmentRequest>

Which should return similar reply:

HTTP/1.1 200 OK

<appointment>
   <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
   <patient id = "jsmith"/>
</appointment>

While Level 1 is aimed mostly at the system decomposition, Level 2 is about HTTP verbs used for calls - it attempts mapping the HTTP verbs as closely as possible to the way they are used in HTTP itself.

At Level 2, the use of GET for a [query] request... is crucial. HTTP defines GET as a safe operation, that is it doesn't make any significant changes to the state of anything. This allows us to invoke GETs safely any number of times in any order and get the same results each time. An important consequence of this is that it allows any participant in the routing of requests to use caching, which is a key element in making the web perform as well as it does. HTTP includes various measures to support caching, which can be used by all participants in the communication. By following the rules of HTTP we're able to take advantage of that capability.

The request in this case is:

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1

The reply is the same as it would have been with POST:

HTTP/1.1 200 OK

<openSlotList>
   <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
   <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

To establish an appointment, an HTTP verb that changes state, a POST or a PUT, should be used. In his article, Fowler is using POST:

POST /slots/1234 HTTP/1.1

<appointmentRequest>
   <patient id = "jsmith"/>
</appointmentRequest>

Which can return something like:

HTTP/1.1 200 OK

<appointment>
   <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
   <patient id = "jsmith"/>
</appointment>

According to Fowler, an important point about using POST is:

... use of an HTTP response code to indicate [request outcome]... Rather than using a return code of 200 but including an error response, at level 2 we explicitly use some kind of error response like this. It's up to the protocol designer to decide what codes to use, but there should be a non-2xx response if an error crops up. Level 2 introduces using HTTP verbs and HTTP response codes... The key elements that are supported by the existence of the web are the strong separation between safe (eg GET) and non-safe operations, together with using status codes to help communicate the kinds of errors you run into.

He also notes that Level 2 exposes some inconsistencies:

REST advocates talk about using all the HTTP verbs. They also justify their approach by saying that REST is attempting to learn from the practical success of the web. But the world-wide web doesn't use PUT or DELETE much in practice. There are sensible reasons for using PUT and DELETE more, but the existence proof of the web isn't one of them.

Finally Level 3 introduces what is often referred to as HATEOAS (Hypertext As The Engine Of Application State). As every resource in REST has its own URI, instead of returning resource ID and allow the consumer to calculate this URI itself, the Level 3 response directly returns the URI of the resource, that can be used for the next operation:

HTTP/1.1 200 OK

<openSlotList>
   <slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
      <link rel = "royalhope.nhs.uk/linkrels/slot/book" uri = "slots/1234"/>
   </slot>
   <slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
      <link rel = "royalhope.nhs.uk/linkrels/slot/book" uri = "slots/5678"/>
   </slot>

Fowler explains that:

The point of hypermedia controls is that they tell us what we can do next, and the URI of the resource we need to manipulate to do it. Rather than us having to know where to post our appointment request, the hypermedia controls in the response tell us how to do it.... One obvious benefit of hypermedia controls is that it allows the server to change its URI scheme without breaking clients... A further benefit is that it helps client developers explore the protocol. The links give client developers a hint as to what may be possible next.

The model presented by Fowler defines three design techniques (levels) for REST services:

  • Level 1 tackles the question of handling complexity by using divide and conquer, breaking a large service endpoint down into multiple resources.
  • Level 2 introduces a standard set of verbs so that we handle similar situations in the same way, removing unnecessary variation.
  • Level 3 introduces discoverability, providing a way of making a protocol more self-documenting.

Rate this Article

Adoption
Style

BT