Building a simple REST API

In my previous article I showed how to build a simple RESTful microservice. For sake of simplicity I skipped some good practices for building REST APIs and I feel a bit guilty about that. I think that topic deserves its own article. And here we go... we take a look at the previous API and try to do things better.


API Requirements

What is the business case of our API? It's a simple public API for a blog (actually this blog). The public here means, it allow the unauthorized uses to read the content. Not more, not less.

The administration API is built out of this scope and I will maybe talk about that in another article later. For the administration API we can expect:

Problems with the Old API

When we don't talk about the missing AuthN/AuthZ, we find a lack of hyper-navigation in the API. Without knowing how the endpoints look like (even it follows the REST recommendations) there is no way how to navigate the client automatically.

Calling the API self results to the response:

GET /api/ HTTP/1.1
HTTP/1.1 403 Forbidden

This is not so bad, because we built a microservice API only for the articles management. In this case, we need a billboard endpoint. The billboard shows information about the blog and navigation to other endpoint.

Another problem is with the navigation, resp. no navigation, within the API. When we call the /api/articles endpoint, the response look like:

[
  {
    "id" : 4,
    "title" : "New article",
    "summary" : "Lorem ipsum",
    "body" : null,
    "createdAt" : "2018-05-03",
    "categoryId" : 1,
    "author" : {
      "id" : 1,
      "name" : "Tomas Tulka",
      "email" : "tomas.tulka@gmail.com"
    }
  },
  ... another articles come here
]

First problem is with the structure itself. What does the attribute body there? It's always null because we expose the whole business object into the API even when some attributes are never set by the underlying service.

Second, the response contains all the data, but we have no idea how to get the detail of an article, next page of the collection or how to filter the articles by the category ID.

New API 

The Billboad

To fulfill our needs we can design the response of the billboard endpoint /api as following:

{
  "version" : "1.0",
  "href" : "/api",
  
  "title" : "My Blog",
  "description" : "A small blog about programming.",
  
  "categories" : [ {
      "id" : 1, 
      "name" : "Programming"
    }, {
      "id" : 2,
      "name" : "Another stuff"
    } ],

  "authors" : [ {
      "name" : "Tomas Tulka",
      "email" : "tomas.tulka@gmail.com",
      "id" : 1 
    } ],
      
  "links" : [ {
      "rel" : "articles",
      "href" : "/api/articles"
    } ] }

The JSON document structure is pretty straightforward: the top-level entities are the blog content elements (categories and authors).

The navigation is represented by the entity links, the rel attribute tells the logical name of the item and href attribute points to the resource URL.

The Articles Collection

Now, what we take a look at the /api/articles endpoint: 

{
  "version" : "1.0",
  "href" : "/api/articles",
  
  "articles" : [ {
      "href" : "/api/articles/4",
      "data" : {
        "id" : 4,
        "title" : "New article",
        "summary" : "Lorem ipsum",
        "createdAt" : "2018-05-03",
        "category" : {
          "id" : 1,
          "name" : "Programming"
        },
        "author" : {
          "id" : 1,
          "name" : "Tomas Tulka",
          "email" : "tomas.tulka@gmail.com"
        }
      }      
    },
    ... another articles    
  ],
  
  "queries" : [ {
      "rel" : "page",
      "name" : "page"
    }, {
      "rel" : "category",
      "name" : "categoryId",
    }, {
      "rel" : "author",
      "name" : "authorId",
    } ],
  
  "links" : [ {
      "rel" : "next",
      "href" : "/api/articles?page=3"
    }, {
      "rel" : "previous",
      "href" : "/api/articles?page=1"
    }, {
      "rel" : "first",
      "href" : "/api/articles"
    }, {
      "rel" : "last",
      "href" : "/api/articles?page=10"
    } ] }

The endpoint have a set of query parameters listed in the queries. Practically it means you can filter the articles by calling the same endpoint with query parameters like /api/articles?categoryId=1&author=1. The values for every query type are known already from the billboard response.

The entity links brings the navigation (pagination).

The Article Detail 

Last but not least is the /api/articles/{id} article detail endpoint:

{
  "version" : "1.0",
  "href" : "/api/articles/4",
  
  "data" : {
    "id" : 4,
    "title" : "New article",
    "summary" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
    "body" : "Lorem ipsum",
    "createdAt" : "2018-05-03",
    "category" : {
      "id" : 1,
      "name" : "Programming"
    },
    "author" : {
      "id" : 1,
      "name" : "Tomas Tulka",
      "email" : "tomas.tulka@gmail.com"
    }
  } }

Further Reading

Source Code

You can find the current implementation (PHP) on GitHub.

Enjoy!