From Resource Hierarchy to RESTful API

Rain Wu
Random Life Journal
5 min readJul 22, 2020

--

This time, I will try to transform the resource hierarchy from the previous article “Construct the Resource Hierarchy” into a set of practical RESTful APIs. If you haven’t read this series of articles, I would recommend you start from the first part “From Requirements to Database Schema”.

Photo by Unsplash

REST

Representational State Transfer (REST) is an architectural approach or concept that base on resources, we can identify the resource via URI and transfer its state. Some principles already are proposed for best practice, such as use nouns for endpoints, prefer to use plurals, and define actions by HTTP methods.

Photo by Unsplash

Here’s another article talks about the practice of RESTful APIs:

With our resource hierarchy, we can simply map out the endpoints:

/users
/users/{USER_ID}
/publications
/publications/{PUBLICATION_ID}
/stories
/stories/{STORY_ID}
/tags

I am used to distinguishing the resources of collection and the particular one by an identifier, such as /users and /users/{USER_ID}. It allows us to get the users’ collection or append a new user at the collection level, and do some detail operations on a singular user resource.

Actions

Photo by Unsplash

Then we add the HTTP method:

GET    /users
POST /users
GET /users/{USER_ID}
PUT /users/{USER_ID}
DELETE /users/{USER_ID}
GET /publications
POST /publications
GET /publications/{PUBLICATION_ID}
PUT /publications/{PUBLICATION_ID}
DELETE /publications/{PUBLICATION_ID}
GET /stories
POST /stories
GET /stories/{STORY_ID}
PUT /stories/{STORY_ID}
DELETE /stories/{STORY_ID}
GET /tags
POST /tags

One of the things to note is that the HTTP POST method should be treated as an append operation, so we should deploy the function of creating a user by

POST   /users

not

POST   /users/{USER_ID}

The resource /users/{USER_ID} still not exist before creation, we should not specify a non-exist resource or even do actions on it. Do not forgive the query requirements, the frontend may ask for the stories with a particular tag. To achieve that, the query parameter is a great choice:

GET    /stories?user_id={USER_ID}&publication_id={PUBLICATION_ID}&tag_id={TAG_ID}

This can make up the lack of relationship of flattened structure, and also a flexible way to cooperate with different query key.

Interaction

Finally, let’s focus on the request and response data, I prefer to use JSON format because of the ease of object construction and value extraction. Here’s an example of creating a user:

{
"name": "Rain Wu",
"introduction": "A technical writer."
}

This is the necessary part to provide enough information for creating a new user resource, and of course, you can add other keys as you want. Now it’s the server’s turn, what content should our server response?

Photo by Unsplash

I may respond with a JSON object like below if a new user resource was created successfully:

{
"status": {
"status_code": 201,
"message": "New user created.",
},
"data": {
"name": "Rain Wu",
"introduction": "A technical writer."
}
}

or if receive a user name with invalid characters:

{
"status": {
"status_code": 400,
"message": "Invalid user name.",
},
"data": {
"name": "Rain Wu $$",
"introduction": "A technical writer."
}
}

The status section can let the client understand the result easily, and the data section is for acknowledgment. Maybe it is clean and tidy enough for judging the result of the operation and decide what to do next, almost all of the services under development I participated in in the past implement to this level.

But this is still a long way from a qualified REST API.

HATEOAS

The section of Hypermedia as the Engine of Application State (HATEOAS) in Roy Fielding’s thesis clearly points out that REST needs to show the nature of hypermedia. In addition to the acceptable operations of the resource itself, also need to describe the relationship with other resources, which is similar to the hyperlink of HTML.

Without the constraints of HATEOAS, API is not a mature REST concept implementation.

But it is too hard to consider about HATEOAS at the beginning, the relationships between the resource could be changed frequently, this can cause a heavy maintenance burden. I totally agree with that, so I always sketch a prototype first, then update them one by one to meets the specification.

Photo by Unsplash

Hypertext Application Language (HAL) provides a set of conventions for expressing hyperlink and often used to construct the response of REST, and it is also human friendly.

Let’s insert the _link section:

{
"_links": {
"self": {
"href": "/users"
},
"user": {
"href": "/users/@RainWu"
},
"follow_from_user": {
"href": "/users?follow-from-user=@RainWu"
},
"follow_to_user": {
"href": "/users?follow-to-user=@RainWu"
}
},
"status": {
"status_code": 201,
"message": "New user created.",
},
"data": {
"name": "Rain Wu",
"introduction": "A technical writer."
}
}

Now the user and their browser or their mobile app can obtain the URI directly by extracting value from response JSON object with a particular key.
As long as the structure of the JSON object does not change, even if we make subsequent changes, it will not affect.

Photo by Unsplash

At this moment, we already echoed the concept of HATEOAS that allows the client-side to adapt automatically, they don’t have to check the document and hard code the URI by themselves. But HATEOAS is more than that, some resources may embedded others, a.k.a sub-resource.

Our flattened structure does not contain that thing, but we can add a supposed setting subresource under the user for example, and the _embedded key would be used:

{
"_links": {
"self": {
"href": "/users"
},
"user": {
"href": "/users/@RainWu"
},
"follow_from_user": {
"href": "/users?follow-from-user=@RainWu"
},
"follow_to_user": {
"href": "/users?follow-to-user=@RainWu"
}
},
"status": {
"status_code": 201,
"message": "New user created.",
},
"data": {
"name": "Rain Wu",
"introduction": "A technical writer."
},
"_embedded": [
{
"setting": {
"_links": {
"self": {
"href": "/users/@RainWu/setting"
}
},
"email": "s0958334772@gmail.com",
"medium digest": "weekly",
"recommanded reading": True
}
}
]
}

It was basically fulfilled the requirements of HATEOAS now, there are still other things such as links to documentation could be appended, but it depends on the different choices of each team, and the above is enough for my simple content platform.

Conclusion

This is the end of my practice for RESTful API design, we go through the whole process by four steps:

  • Requirements analysis
  • Design database schema
  • Construct resource hierarchy
  • Design RESTful APIs

Hope it helps you :)

--

--

Rain Wu
Random Life Journal

A software engineer specializing in distributed systems and cloud services, desire to realize various imaginations of future life through technology.