Minimal APIs and HATEOAS

HATEOAS stands for Hypermedia As The Engine Of Application State. It is one of the most important concepts in REST and what actually makes your HTTP APIs completely conform to REST constraints. While preparing for my session about Minimal APIs at the DotNetDevs Austria user group, I was intrigued about whether I could build a Minimal API that implements HATEOAS. Let's see whether I have succeeded in doing that.

I am implementing a RESTful Minimal API to act upon text messages where my text message class looks as shown below.

I have a set of endpoints set up using Minimal APIs as shown below, as my starting point.

With HATEOAS implemented, every GET request to a resource comes back with information about the requested resource and what actions can be performed on the resource, what are the related resources etc. The idea is that the consuming client can then use these links to make changes to the application state than using hardcoded links. The hypermedia acts as the engine to change the application state, hence the name HATEOAS. 

Let us now extend these minimal endpoints to have hypermedia in place for the two GET endpoints - to read all the messages and to read a message by its id.

With HATEOAS a sample response from the API looks as shown below.

We will first create a class that represent a single link item in the array of links as show below.

We also need a class that represents a collection of such LinkDto objects as shown below.

We will first amend our endpoint to read a single text message object. To begin with, we inherit out TextMessage class from the LinkResourceDto class we created above.

We can then amend our endpoint to include links as shown below. Rather than hardcoding the routes into our LinkDto object we will make use of the LinkGenerator class to build the url by using the endpointname associated with each endpoint. Each endpoint has been given a name using the WithName() method. 

The response from the endpoint now looks as shown below.

We now need to amend our endpoint that reads all messages. This is a bit more complicated as we have a list of messages and we need to make sure that each of the messages have links associated with them. In addition to this, we need to make sure there is a link to the current collection(GET /messages) and any other related collections that might be there. For e.g. the TextMessage class could be related to a User class and there might be a /users set of endpoints with CRUD operations. In such a case a GET request to /messages should generate

  • a list of messages and the associated links
  • link to messages collection(GET /messages)
  • link to users collection(GET /users)

So we have to introduce a wrapper class as shown below.

We can now amend our endpoint to read all messages as shown below. Note that the endpoint now returns a Ok<LinkedCollectionResourceWrapperDto<TextMessageDto>> as opposed to Ok<List<TextMessageDto>>.

And that is the response now from our endpoint that read all the messages.

Note that this is just sample code and an idea I was playing around with. The code can be refactored and improved but I am sure it can act as a good starting point. 

Picture Courtesy : Photo by NASA on Unsplash