REST API Maturity Levels : From 0 to 5

swagger-logoThis article is about RESTful maturity. It’s not a definition of levels of REST itself, but a simple approach with examples with HTTP, Spring.

Diapositive1.PNG

Content

  • What’s an API ?
  • What’s REST?
  • Steps toward REST
  • REST API Level 0
  • REST API Level 1
  • REST API Level 2
  • REST API Level 3
  • REST API Level 4
  • REST API Level 5

What’s an API ?

“defined methods of communication between various software components”

Wikipedia


What’s REST?

REST or RESTful is not a standard, but a WebAPI

  • Client-Server Architecture
  • Stateless
  • Cacheability
  • Layered System
  • Uniform Interface

“By using a stateless protocol and standard operations, REST systems aim for fast performance, reliability, and the ability to grow, by re-using components that can be managed and updated without affecting the system as a whole, even while it is running.”

Wikipedia


Steps toward REST

First levels are now industry standards conventions, the lasts levels are more business oriented.
pictures.png
See more at https://martinfowler.com/articles/richardsonMaturityModel.html


REST API Level 0

Using HTTP 1.1 protocol !

  • Client-Server : request-response protocol
  • Uniform : web standard
  • Layered System : HTTP hide underlying technology (database, cache, file, etc)

Diapositive2.PNG

CREATE : POST /api/user
READ :   POST /api/user
UPDATE : POST /api/user
DELETE : POST /api/user

Example : SOAP over HTTP

Request

ScreenShot004

Response

ScreenShot005.png

https://www.tutorialspoint.com/soap/soap_examples.htm


REST API Level 1

1 Resource = 1 URI (like /api/users/…) !

  • Stateless : only 1 URI, so no state in path (no /user/creation, just /users)
  • Uniform: URI is universal, URI don’t change

Diapositive3.PNG

CREATE : POST /api/users/create
READ   : GET /api/users, GET /api/user/read
UPDATE : POST /api/users/update
DELETE : POST /api/users/delete

SEARCH : GET or POST /api/users?sort=name

Details

  • static path : no /user and  /users and /user-new …just /users
  • redirection : no /users and /users/ …just /users
  • case insensitive :  no /User/RoleGroup …just /user/role-group
  • format/parameters: no /users/xml/sort-by-name …just /user?format=xml&sort=name

Example : Same HTTP action for two different actions!

Update Request

POST /api/users HTTP/1.1
Host api.host.com

...

Delete Request

POST /api/users-delete HTTP/1.1
Host api.host.com

...

REST API Level 2

Actions inside URI (like /api/users {GET, PUT, POST, DEL}) !

  • Uniform: using HTTP method for CRUD methods
  • Cacheability : cache on READ, Idempotence on Actions

Diapositive4.PNG

CREATE : PUT /api/users
READ   : GET /api/users/42
UPDATE : POST /api/users/42
DELETE : DEL /api/users/42
SEARCH : GET /api/users?sort=name

Details

  • no action in URL : no /users/delete or /users-delete …just DEL /users
  • list/array : no unique response object …just a list of IDs, then client will get datas (if not in cache) by calling resource/IDs
  • Cache: resources ID in response instead of data
  • Idempotence : actions can be applied multiple times without changing the result (ex: delete user/42 multiple time will only delete 1 row in database)

Example : GitHub Single Object (Uniforme)

Request

GET /users/DamienFremont HTTP/1.1
Host api.github.com

Response (then cached for next usages)

HTTP/1.1 200 OK

{
  "login": "DamienFremont",
  "id": 123456,
  "url": "https://api.github.com/users/DamienFremont",
  // ...
}

Example : GitHub Collection (Cacheability)

Request

GET /users/DamienFremont/followers HTTP/1.1
Host api.github.com

Response

HTTP/1.1 200 OK

[
 "https://api.github.com/users/jeanmichel72",
 "https://api.github.com/users/alien42",
  // ...
]

instead of this (not cacheable because some parts could change over time. ex: user update)

HTTP/1.1 200 OK

[
 { "login": "jeanmichel72", "id": 123456, ...},
 { "login": "alien42", "id": 123456, ...},
 // ...

REST API Level 3

RESTful or HATEOAS (Hypermedia As Engine of Application State) !

  • Uniform : Self-descriptive

Diapositive5.PNG

CREATE : PUT /api/users/create
READ : GET /api/users/42
UPDATE : POST /api/users/42
DELETE : DEL /api/users/42
SEARCH : GET /api/users?sort=name

Self-descriptive Response Body:
HTTP/1.1 200 OK

{
  "_embedded" : {
    "users" : [ {
      "username" : "admin", ...
      "_links" : {
        "self" : { "href" : "http://localhost:8080/api/users/1" },
        "user" : { "href" : "http://localhost:8080/api/users/1" }
      }
    }, {
      ...
    } ]
  },
  "_links" : { "self" : { "href" : "http://localhost:8080/api/users{?page,size,sort}", },
  "search" : { "href" : "http://localhost:8080/api/users/search" } },
  "page" : {
    "size" : 20,
    "totalElements" : 27,
    "totalPages" : 2,
    "number" : 0
  }
}

Details

  • Self-descriptive : API index summarizes all the resources
  • No question nor  interpretation required about navigability, URI, URL, format
  • No need for documentation

Example : Spring Self-descriptive Generated API

Spring API Request

<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>
GET /api/ HTTP/1.1
Host localhost:8080

Spring API Response

HTTP/1.1 200 OK

{
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/api/users{?page,size,sort}",
      "templated" : true
    }
  }
}

REST API Level 4

API Versionning !

  • Uniform : Self-descriptive

Diapositive6.PNG

CREATE : PUT /api/users/create
READ : GET /api/users/42
UPDATE : POST /api/users/42
DELETE : DEL /api/users/42
SEARCH : GET /api/users?sort=name
...Self-descriptive Response body :
{
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/api/users{?page,size,sort}"
    }
  }
}

...Version Request URI:
GET /api/v2/user.json HTTP/1.1
Host api.host.com

Details:  2 solutions

  • Path-based approch, in which the client defines the version in URL:
    http://localhost:9001/api/v1/user
    http://localhost:9001/api/v2/user
  • Content-Type header, in which the client defines the version in Accept header:
    http://localhost:9001/api/v1/user with 
    Accept: application/vnd.app-1.0+json OR application/vnd.app-2.0+json

Note: Since i am working a lot with rest documentation tools, i prefer to use the first approach.

Example : Implementation with Spring

import io.swagger.annotations.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@Controller
@Api(value = "user", description = "Operations about users")
public class UserController {

    @ResponseBody
    @RequestMapping(method = RequestMethod.GET, value = "/api/v1/user")
    @ApiOperation(value = "Returns user", notes = "Returns the user", tags = <span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>{"GET", "User"})
    public String getUserV1() {
         return "User V1";
    }

    @ResponseBody
    @RequestMapping(method = RequestMethod.GET, value = "/api/v2/user")
    @ApiOperation(value = "Returns user", notes = "Returns the user", tags = {"GET", "User"})
    public String getUserV2() {
         return "User V2";
    }
 }

Or you can duplicate/redirect to the old V1 classes.


REST API Level 5

Add behaviors to API responses !

Diapositive7.PNG

CREATE : PUT /api/users/create 
READ : GET /api/users/42 
UPDATE : POST /api/users/42 
DELETE : DEL /api/users/42 
SEARCH : GET /api/users?sort=name 
...Self-descriptive Response body : 
{ 
  "_links" : { 
    "users" : { 
      "href" : "http://localhost:8080/api/users{?page,size,sort}" } } } 
...Version Request URI: 
GET /api/v2/user.json HTTP/1.1 
Host api.host.com

...Behavior in Response body 
HTTP/1.1 201 OK 
{ 
  "_links" : { 
    "self" : { "href" : "http://localhost:8080/api/users/1" }
}

Details

  • user/app can discover API by using it
  • next actions are directly provided to client, so no need to store all static URL in client source code, and after a CREATE you can directly use the resource with the newly provided READ URL

Example : User CREATE return READ/UPDATE/DELETE behaviours

Spring API Request

PUT /api/user HTTP/1.1
Host localhost:8080

{
  "firstname" : "Damien",
  "lastname" : "FREMONT"
}

Spring API Response, with URL and newly created ID

HTTP/1.1 201 OK

{
  "_links" : {
    "self" : { "href" : "http://localhost:8080/api/users/1" },
    "user" : { "href" : "http://localhost:8080/api/users/1" },
  }
}

Conclusion

REST API self-descriptive can be enhanced with tools; like Swagger for autodocumentation and Spring for implementation.

Swagger

Screenshot445-API_documentation_43.png

Spring REST Data generated API is a good example of RESTful, with uniform URI, discoverability via JSON response meta-data.

Spring Implementation

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository&amp;lt;User, Long&amp;gt; {
   User findByUsername(String username);
}

Spring API Request

GET /api/ HTTP/1.1
Host localhost:8080

Spring API Response

HTTP/1.1 200 OK

{
  "_links" : {
    "profiles" : {
      "href" : "http://localhost:8080/api/profiles{?page,size,sort}",
      "templated" : true
    },
    "users" : {
      "href" : "http://localhost:8080/api/users{?page,size,sort}",
      "templated" : true
    }
  }
}

Spring API Request

GET /api/users HTTP/1.1
Host localhost:8080

Spring API Response

HTTP/1.1 200 OK
{
  "_embedded" : {
    "users" : [ {
      "dateCreated" : null,
      "lastUpdated" : null,
      "username" : "admin",
      "enabled" : true,
      "accountNonExpired" : true,
      "credentialsNonExpired" : true,
      "accountNonLocked" : true,
      "roles" : [ ...],
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/api/users/1"
        },
        "user" : {
          "href" : "http://localhost:8080/api/users/1"
        }
      }
    }, {
      ...
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/users{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:8080/api/profile/users"
    },
    "search" : {
      "href" : "http://localhost:8080/api/users/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 27,
    "totalPages" : 2,
    "number" : 0
  }
}

References

https://en.wikipedia.org/wiki/Application_programming_interface

https://fr.wikipedia.org/wiki/Representational_state_transfer

http://www.baeldung.com/rest-versioning

https://stackoverflow.com/questions/20198275/how-to-manage-rest-api-versioning-with-spring

https://martinfowler.com/articles/richardsonMaturityModel.html

https://www.infoq.com/fr/presentations/bow-virginie-bardales-rest-world?utm_source=presentations_about_best-of-web&utm_medium=link&utm_campaign=best-of-web

On GitHub

https://github.com/DamienFremont/blog/tree/master/20171123-rest-api-maturity-levels-from-0-to-5

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s