React & Spring Boot Hateoas Driven Fullstack Application on Kubernetes
Hello everyone, In this article, we will develop a hateoas driven fullstack application using React and Spring Boot and deploy this application on Kubernetes.
Richardson Maturity Model
Leonard Richardson analyzed a hundred different web service designs and divided these designs into four categories. These categories are based on how much the web services are REST compliant.
This model of division of REST services to identify their maturity level — is called Richardson Maturity Model.
Richardson used three main factors to decide the maturity of a service. These factors are
The more a service employs these factors — the more mature it shall be considered.
Maturity Levels
In his analysis, Richardson described total 4 maturity levels as given below:
- Level Zero
- Level One
- Level Two
- Level Three
Note that Roy Fielding has already made it very clear that level 3 RMM is a pre-condition of REST.
Level Three
Level three of maturity makes use of all three, i.e. URIs and HTTP, and HATEOAS.
Level three is the most mature level of Richardson’s model, which encourages easy discoverability. This level makes it easy for the responses to be self-descriptive by using HATEOAS.
Level three services lead the service consumers through a trail of resources, causing application state transitions as a result.
Hateoas
The term HATEOAS stands for the phrase Hypermedia As The Engine Of Application State. To understand this further, we first need to understand the meaning of Hypermedia.
The single most important reason for HATEOAS is loose coupling. If a consumer of a REST service needs to hard-code all the resource URLs, then it is tightly coupled with your service implementation. Instead, if you return the URLs, it could use for the actions, then it is loosely coupled. There is no tight dependency on the URI structure, as it is specified and used from the response.
HAL — Hypertext Application Language
When you design a RESTful service, there is a need to specify how to return data and links corresponding to a request. HAL is a simple format that gives an easy, consistent way to hyperlink between resources in your REST API. Here is an example
With HAL, you have a few categories of representations:
- Links: Specified as a combination of
- Target — Given as a URI
- Relation — A name
- Embedded Resources: Other resources contained within a given REST resource
- State: The actual resource data
If you happen to use the Spring Framework to develop your REST service, then Spring HATEOAS is a good engine to use for your service.
Spring Boot HATEOAS
First, let’s add the Spring HATEOAS dependency to our pom.xml
We will use SpringDoc for api documentation. Springdoc supports hateoas.we simply add the springdoc-openapi-ui and springdoc-openapi-hateoas ui dependency to our pom.xml:
Native images provide various advantages like an instant startup and reduced memory consumption.We will use Spring Native to compile and build native images using Buildpacks and GraalVM’s native build tools.
Let’s add the spring-native Maven dependency and required plugins,repos.
Here, we’ll use the tiny builder out of the various available builders like base and full to build a native image.Also, we enabled the upx by providing the UPX value to the BP_BINARY_COMPRESSION_METHOD environment variable.
UPX is an advanced file compressor that compresses executable files.When spring native or golang projects are compiled, since they produce executable programs directly, we can reduce the size of these programs by 50–70% with upx.
we’ll add property to use Java 17 for compilation
Spring HATEOAS offers three abstractions for creating the URI — RepresentationModel, Link, and WebMvcLinkBuilder. We can use these to create the metadata and associate it to the resource representation.
Adding Hypermedia Support to a Resource
The CapabilityDto resource extends from the RepresentationModel class to inherit the add() method. So once we create a link, we can easily set that value to the resource representation without adding any new fields to it.
Creating Links
Spring Hateoas provides the WebMvcLinkBuilder — which simplifies building URIs by avoiding hard-coded the links.
The following snippet shows building the customer self-link using the WebMvcLinkBuilder class:
linkTo(methodOn(CapabilityController.class).getCapability(resource.getContent().getId())).withSelfRel()
Let’s have a look:
- the linkTo() method inspects the controller class and obtains its root mapping
- the slash() method adds the customerId value as the path variable of the link
- finally, the withSelfMethod() qualifies the relation as a self-link
Assemblers
it’s not about assembly language, but about a special kind of class that converts our resource to RepresentationModel.
One of such assemblers is SimpleRepresentationModelAssembler. Its implementation goes as follows:
In this case, our entity will be wrapped in an EntityModel
(this class extends RepresentationModel
) to which the links specified by us in the addLinks()
will be added. Here we overwrite two addLinks()
methods – one for entire data collections and the other for single resources. Then, as part of the controller, it is enough to call the toModel()
or toCollectionModel()
method (addLinks()
are template methods here), depending on whether we return a collection or a single representation.
Spring hateoas provides automatic pagination links, we must use PagedModel
provided by spring hateoas module which helps in creating representations of pageable collections.
PagedResourcesAssembler accepts the JPA entities list or Dtos List, and convert it to PagedModel.
Spring HATEOAS in Action
- Finally,
PagedModel,EntityModel,CollectionModel
is returned as API response from web controller.
Verify links
Next, let’s invoke the getAllCapabilitiesWithPagination() method
curl http://localhost:8080/api/paged-capabilities
And examine the result:
{
"_embedded": {
"capabilities": [
{
"id": 1,
"techStack": "ReactJS",
"numOfDevelopers": 70,
"numOfAvailableDevelopers": 20,
"_links": {
"self": {
"href": "http://localhost:8080/api/capabilities/1"
},
"capabilities": {
"href": "http://localhost:8080/api/capabilities"
}
}
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/paged-capabilities?page=0&size=1"
},
"self": {
"href": "http://localhost:8080/api/paged-capabilities?page=0&size=1"
},
"next": {
"href": "http://localhost:8080/api/paged-capabilities?page=1&size=1"
},
"last": {
"href": "http://localhost:8080/api/paged-capabilities?page=12&size=1"
},
"capabilities": {
"href": "http://localhost:8080/api/capabilities"
}
},
"page": {
"size": 1,
"totalElements": 13,
"totalPages": 13,
"number": 0
}
}
Spring Hateoas Conclusion
We saw hateoas driven rest api that adding HATEOAS links using spring boot hateoas module is very much easy and requires very less time and effort. In return, it increases the discoverability and usefulness of APIs by many folds.
React Hateoas
/api/paged-capabilities api returns the links associated with the capability resource.
Without HATEOAS, the client needs to know the URIs beforehand to call the correct endpoints. Instead of hardcoding the URIs in our component, we can refer to the links contained within our capability resource
We’ll start by writing a thunk function that makes an api call to our /api/paged-accounts
endpoint to request an page of capality object, and then dispatch an action containing that array and links.
In the code below, we created a capabilityReducer.GET_CAPABILITIES action will return the page, payload and links.
AddCapability component gets the post Link from the redux store and passes this link as a parameter to the addCapabilityHandler.
addCapabilityHandler executes addCapability() asynchronous HTTP request .Then, It dispatches ADD_CAPABILITY action to update the redux store.
Paging
Spring hateoas provides PagedModel to enable automatic pagination links.
We can retrieve a list of capabilities from GET /api/paged-capabilities
, which features basic pagination:
The outer links array in the response payload includes references to the first, previous, current (via "rel" : "self"
), next, and last resources pages
React Hateoas Conclusion
100% HATEOAS IS compatible with React & Flux, HATEOAS is compatible with Angular, HATEOAS is compatible with JQuery and even vanilla JS.
HATEOAS doesn’t not impose any technical or implementation requirements on a consuming client.
HATEOAS is in fact simply a concept to which you can design your API (you can use one of several standards though like HAL)
Basically if you can call an API then you can implement a HATEOAS client.
So how to get there:
- Step 1, how would you normally do an API call in React? Do it the same way.
- Step 2, interrogate response.
- Step 3, based on response, respond in the UI appropriately.
Heroku
I deployed to heroku the hateoas fullstack application.You can access the web ui of the application and the swagger documentation via the links below.
The Hateoas Fullstack application can be accessed from this link.
https://hateoas-fullstack-ui.herokuapp.com
The swagger ui can be accessed from this link.
https://hateoas-fullstack-api.herokuapp.com/swagger-ui.html
Kubernetes Deployment with Helm
Helm is a package manager for Kubernetes that allows developers and operators to more easily package, configure, and deploy applications and services onto Kubernetes clusters.
You can deploy hateoas fullstack app by running the following bash command
./helm-apply.sh
You can upgrade hateoas fullstack apps (if you have made any changes to the generated manifests) by running the following bash command
./helm-upgrade.sh
The Hateoas Fullstack application can be accessed with ingress from the link below.
http://hateoas-fullstack-ui.github.io
Finally, the full implementation of this article can be found in the GitHub project.
My article ends here. See you in the next articles.