REST API Maturity - Towards The Glory of REST
Tags: webdev, api, backend, rest
Published: 2020-11-14 11:56:35

Is there a way to classify REST APIs based on their architectural maturity?


The **Richardson Maturity Model** developed by [Leonard Richardson](https://www.crummy.com) is a heuristic that is used to indicate how mature a web service is in regards to its REST Architectural style.

The model classifies APIs into 4 different levels (0-3)
* **Level 0** - The Swamp of POX
* **Level 1** - Resources
* **Level 2** - HTTP Verbs
* **Level 3** - Hypermedia Controls

The model identifies Level 3 as the **Glory of REST**

![Capture](https://dev-to-uploads.s3.amazonaws.com/i/aregpuzds2v57k4shgpa.PNG)

###**Level 0** - The Swamp of POX

In this level, HTTP is used only as a transport layer for remote interactions without using any inherent mechanisms of the web, meaning it only tunnels the request and nothing more. This style is usually associated with [RPC (Remote Procedure Calls)](https://en.wikipedia.org/wiki/Remote_procedure_call).
The acronym **POX** stand for **Plain Old XML**, this is because XML was used primarily as the payload of the request/response of the RPC however this idea can really be extended to any other formats, we will be using JSON for the examples.

![pox_diagf](https://dev-to-uploads.s3.amazonaws.com/i/xozxby3r7d3c2q3566ls.jpeg)

Lets assume we have a social media app that lets users follow other users. Before following any specific user, we first want to show a list of all users. The app in this case will expose services at two separate endpoints.
* An endpoint for listing of all users
* An endpoint for following other users
 
First we will initiate a request to the first endpoint to list all users from Canada 
```json
 #Request
 POST /listUsers HTTP/1.1
 {
  "country" : "Canada"
 }   
```

After the service receives the request, it will retrieve a list of all users living in Canada

```json
 #Response
 HTTP/1.1 200 OK
 {
  [
    { "name" : "John Smith", "id" : 45 },
    { "name" : "Jane Doe", "id" : 22 }
  ]
 }   
```
The next step now is to call the endpoint for following the desired user
```json
 #Request
 POST /followUser HTTP/1.1
 {
  "id" : 22
 }   
```
the response could be either a **success** or an **error**, in case of **success** it will look something like this:
```json
 #Response
 HTTP/1.1 200 OK
 {
  "status" : "success"
 }   
```
in case of an **error** it will look something like this:
```json
 #Response
 HTTP/1.1 200 OK
 {
  "status" : "error",
  "message" : "already following"
 }   
```
Notice that the request HTTP Method is always **POST** and the service HTTP status code is always **200** regardless of the state of the process.

###**Level 1** - Resources

In this level, the service starts working with resources denoted in the URI of the request. Note that resource identification is part of one of the main REST constraints which is having a  [**Uniform Interface**](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_2b)

![resources](https://dev-to-uploads.s3.amazonaws.com/i/r05c15qcc94vcwuxjfc7.jpeg)

The first request will be to the /users endpoint to get the list of all users, the response will be similar to the one before
```json
 #Request
 POST /users HTTP/1.1
 {
  "country" : "Canada"
 }   
```
```json
 #Response
 HTTP/1.1 200 OK
 {
  [
    { "name" : "John Smith", "id" : 45 },
    { "name" : "Jane Doe", "id" : 22 }
  ]
 }   
```

Now following the desired user by his id

```json
 #Request
 POST /users/22 HTTP/1.1
 {}   
```
The response could be either a **success** or an **error**
```json
 #Response
 HTTP/1.1 200 OK
 {
  "status" : "success"
 }   
```
```json
 #Response
 HTTP/1.1 200 OK
 {
  "status" : "error",
  "message" : "already following"
 }   
```
Notice that the HTTP Method as well as the HTTP status code remain unchanged just like Level 0 (POST and 200)

###Level 2 - HTTP Verbs

Up until now, we have been strictly using HTTP POST, the idea was using HTTP only to tunnel the interactions through it and not utilizing any of its web mechanisms ( **i.e**:  verbs and status codes ).

In this level we make sure to be as close as possible to the _intent_ of the request in regards to the HTTP Verbs

![verbs](https://dev-to-uploads.s3.amazonaws.com/i/79gndreaxqe3d9qdwuhp.jpeg)
 
The first request is now **GET** request to /users since we are logically reading or getting data, if we need to filter out based on a specific country we can now utilize [**query parameters**](https://en.wikipedia.org/wiki/Query_string)
```json
 #Request
 GET /users?country=canada HTTP/1.1 
```
The response will be a list of users from Canada just like Level 0 and 1 
```json
 #Response
 HTTP/1.1 200 OK
 {
  [
    { "name" : "John Smith", "id" : 45 },
    { "name" : "Jane Doe", "id" : 22 }
  ]
 }   
```

Now when following another user we use POST, you can think of it as if we are logically _creating_ a new follow request

```json
 #Request
 POST /users/22 HTTP/1.1
 {}   
```
The response could be either a **success** or an **error**, in the case of success, it is denoted by the corresponding HTTP status code
```json
 #Response
 HTTP/1.1 200 OK
 { }   
```
In case of an error, a proper HTTP status code can be utilized as well
```json
 #Response
 HTTP/1.1 409 CONFLICT
 {
  "message" : "already following"
 }   
```

###Level 3 - Hypermedia controls
 
Level 3 introduces using something you might have heard about here or there which is [**HATEOAS**](https://en.wikipedia.org/wiki/HATEOAS#:~:text=Hypermedia%20as%20the%20Engine%20of,provide%20information%20dynamically%20through%20hypermedia.) ( Hypermedia As The Engine Of Application State ). Essentially HATEOAS makes the API respond to requests with additional information and resource links that make _exploring_ the API and accessing additional resources easier. Most of the time it can infer to the client as to what "actions" can be done next and where to access them if needed.

![hateoas](https://dev-to-uploads.s3.amazonaws.com/i/nev56q26d5ame2cwfxqv.jpeg)

As before the request to get all users  
```json
 #Request
 GET /users?country=canada HTTP/1.1 
```
Now we have the API respond with relevant hypermedia controls
```json
 #Response
 HTTP/1.1 200 OK
 {
  [
    { 
      "name" : "John Smith", 
      "id" : 45 ,  
      "links" : [
       {
         "rel" : "rels/user/follow",
         "href" : "/users/45",
       }
      ]
    },
    { 
      "name" : "Jane Doe", 
      "id" : 22,  
      "links" : [
       {
         "rel" : "rels/user/follow",
         "href" : "/users/22",
       }
      ]
    },
  ]
 }   
```
The rest of the requests/responses are pretty much the same as the previous level
```json
 #Request
 POST /users/22 HTTP/1.1
 {}   
```
success/failure
```json
 #Response
 HTTP/1.1 200 OK
 { }   
```
```json
 #Response
 HTTP/1.1 409 CONFLICT
 {
  "message" : "already following"
 }   
```
For many engineers, Level 2 seems to be enough and will put the API in a very solid state. You can imagine the complexity HATEOAS would introduce at both ends of the stack. However it has some  benefits that shouldn't be just ignored.
HATEOAS provides the ability to seamlessly update the URI Schema of the APIs without worrying about or breaking clients as well as providing a self documenting API.

###**Closing Thoughts**

Overall i think RMM in itself is not a model that defines how a truly RESTful API should look like, but it provides good guidelines that if followed can lead to a solid API Design head start. 

Personally, i believe Level 3 is not worth the added complexity unless used in a very niche case where having a discoverable API is one of the key requirements of the project at hand.

I think introducing RMM especially to beginners in backend engineering is very important since it can make them think about good API Design and generally what makes an API RESTful by forcing them to atleast think about some of the constraints of REST even though it doesn't define it.