HowToDoInJava

  • Python
  • Java
  • Spring Boot
  • Dark Mode
Home / Spring Boot 2 / Spring Boot REST / Spring Boot HATEOAS Example

Spring Boot HATEOAS Example

In this Spring HATEOAS example, we will learn to add HATEOAS links to existing REST APIs created in a spring boot project. We will use the class ResourceSupport along with ControllerLinkBuilder and org.springframework.hateoas.Link classes provided by spring HATEOAS module.

To demo the creation of links, we will first create few REST APIs and see their output. Then we will apply the HATEOAS links to REST resources and then we will compare the output with and without links.

Table of Contents

Project Structure
Create REST APIs
Add HATEOAS Links to REST resources
Summary

Project Structure

Spring HATEOAS Example - Project Structure
Spring HATEOAS Example – Project Structure

Create REST APIs

In this example, I have created three REST APIs with endpoints as below:

  1. /employees
  2. /employees/{id}
  3. /employees/{id}/report

REST Resource Model

@XmlRootElement (name="employees")
public class EmployeeListVO implements Serializable
{
    private static final long serialVersionUID = 1L;
     
    private List<EmployeeVO> employees = new ArrayList<EmployeeVO>();
 
    public List<EmployeeVO> getEmployees() {
        return employees;
    }
 
    public void setEmployees(List<EmployeeVO> employees) {
        this.employees = employees;
    }
}
@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.NONE)
public class EmployeeVO implements Serializable
{
	private static final long serialVersionUID = 1L;
	
	public EmployeeVO(Integer id, String firstName, String lastName, String email) {
		super();
		this.employeeId = id;
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
	}

	public EmployeeVO() {

	}

	@XmlAttribute
	private Integer employeeId;

	@XmlElement
	private String firstName;

	@XmlElement
	private String lastName;

	@XmlElement
	private String email;

	//removed getters and setters for readability

	@Override
	public String toString() {
		return "EmployeeVO [id=" + employeeId + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email
				+ "]";
	}
}
@XmlRootElement(name="employee-report")
public class EmployeeReport implements Serializable {

	private static final long serialVersionUID = 1L;

	//You can add field as needed
}

REST Controller

@RestController
public class EmployeeRESTController {
	
	@RequestMapping(value = "/employees")
    public EmployeeListVO getAllEmployees()
    {
		EmployeeListVO employeesList  = new EmployeeListVO();

        for (EmployeeVO employee : EmployeeDB.getEmployeeList()) 
        {
            employeesList.getEmployees().add(employee);
        }

        return employeesList;
    }
     
    @RequestMapping(value = "/employees/{id}")
    public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
    {
        if (id <= 3) {
            EmployeeVO employee = EmployeeDB.getEmployeeList().get(id-1);
            return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
        }
        return new ResponseEntity<EmployeeVO>(HttpStatus.NOT_FOUND);
    }
    
    @RequestMapping(value = "/employees/{id}/report")
    public ResponseEntity<EmployeeReport> getReportByEmployeeById (@PathVariable("id") int id)
    {
    	//Do some operation and return report
    	return null;
    }
}

DAO layer

I have created EmployeeDB class to simulate the DAO layer. In real application, it will be more complex code to fetch data from data source.

public class EmployeeDB {

	public static List<EmployeeVO> getEmployeeList() 
	{
		List<EmployeeVO> list = new ArrayList<>();

		EmployeeVO empOne = new EmployeeVO(1, "Lokesh", "Gupta", "howtodoinjava@gmail.com");
		EmployeeVO empTwo = new EmployeeVO(2, "Amit", "Singhal", "asinghal@yahoo.com");
		EmployeeVO empThree = new EmployeeVO(3, "Kirti", "Mishra", "kmishra@gmail.com");

		list.add(empOne);
		list.add(empTwo);
		list.add(empThree);

		return list;
	}
}

Launch Class

@SpringBootApplication
public class Application 
{
	public static void main(String[] args) 
	{
		ApplicationContext ctx = SpringApplication.run(Application.class, args);
	}
}

Maven – pom.xml

Let’s look at pom.xml file used for this project.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.howtodoinjava</groupId>
	<artifactId>springbootdemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springbootdemo</name>
	<url>http://maven.apache.org</url>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
	</parent>

	<properties>
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-hateoas</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>repository.spring.release</id>
			<name>Spring GA Repository</name>
			<url>http://repo.spring.io/release</url>
		</repository>
	</repositories>
</project>

API Outputs

  1. /employees
    {
        "employees": [
            {
                "employeeId": 1,
                "firstName": "Lokesh",
                "lastName": "Gupta",
                "email": "howtodoinjava@gmail.com"
            },
            {
                "employeeId": 2,
                "firstName": "Amit",
                "lastName": "Singhal",
                "email": "asinghal@yahoo.com"
            },
            {
                "employeeId": 3,
                "firstName": "Kirti",
                "lastName": "Mishra",
                "email": "kmishra@gmail.com"
            }
        ]
    }
  2. /employees/{id}
    {
    	"employeeId": 1,
    	"firstName": "Lokesh",
    	"lastName": "Gupta",
    	"email": "howtodoinjava@gmail.com"
    }

Add HATEOAS Links to REST resources

Step 1) Extend resource models with ResourceSupport class

Extend all model classes where you want to add HATEOAS links – with org.springframework.hateoas.ResourceSupport class.

@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.NONE)
public class EmployeeVO extends ResourceSupport implements Serializable
{
	//rest all code is same
}

//...

@XmlRootElement (name="employees")
public class EmployeeListVO extends ResourceSupport implements Serializable
{
	//rest all code is same
}

//...

@XmlRootElement(name="employee-report")
public class EmployeeReport extends ResourceSupport implements Serializable {

	//rest all code is same
}

Step 2) Build and add links in REST controller

To add links, you will need ControllerLinkBuilder and Link classes. Link is the final representation of link to be added in REST resource. Whereas ControllerLinkBuilder helps in building the links using it’s various method based on builder pattern.

Adding links to collection resource

Here we will add two type of links. In first link, the collection resource will point to itself. In second type of links, each resource inside collection will point to it’s URI location where complete representation is available. Also, each resource will have method links which can be followed to perform some operations on individual resource.

@RequestMapping(value = "/employees")
public EmployeeListVO getAllEmployees()
{
	EmployeeListVO employeesList  = new EmployeeListVO();

	for (EmployeeVO employee : EmployeeDB.getEmployeeList()) 
    {
    	//Adding self link employee 'singular' resource
        Link link = ControllerLinkBuilder
        		.linkTo(EmployeeRESTController.class)
        		.slash(employee.getEmployeeId())
        		.withSelfRel();

        //Add link to singular resource
        employee.add(link);
        
      //Adding method link employee 'singular' resource
        ResponseEntity<EmployeeReport> methodLinkBuilder = ControllerLinkBuilder
        		.methodOn(EmployeeRESTController.class).getReportByEmployeeById(employee.getEmployeeId());
        Link reportLink = ControllerLinkBuilder
        		.linkTo(methodLinkBuilder)
        		.withRel("employee-report");

       	//Add link to singular resource
        employee.add(reportLink);
  
        employeesList.getEmployees().add(employee);
    }
    
    //Adding self link employee collection resource
    Link selfLink = ControllerLinkBuilder
    		.linkTo(ControllerLinkBuilder
    		.methodOn(EmployeeRESTController.class).getAllEmployees())
    		.withSelfRel();

    //Add link to collection resource
    employeesList.add(selfLink);
     
    return employeesList;
}

Output:

{
    "employees": [
        {
            "employeeId": 1,
            "firstName": "Lokesh",
            "lastName": "Gupta",
            "email": "howtodoinjava@gmail.com",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/1"
                },
                "employee-report": {
                    "href": "http://localhost:8080/employees/1/report"
                }
            }
        },
        {
            "employeeId": 2,
            "firstName": "Amit",
            "lastName": "Singhal",
            "email": "asinghal@yahoo.com",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/2"
                },
                "employee-report": {
                    "href": "http://localhost:8080/employees/2/report"
                }
            }
        },
        {
            "employeeId": 3,
            "firstName": "Kirti",
            "lastName": "Mishra",
            "email": "kmishra@gmail.com",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/3"
                },
                "employee-report": {
                    "href": "http://localhost:8080/employees/3/report"
                }
            }
        }
    ],
    "_links": {
        "self": {
            "href": "http://localhost:8080/employees"
        }
    }
}

Adding links to singular resource

Adding links to singular resource is exactly same as what we saw in earlier section. Singular resource representations typically have more information/fields plus additional links.

@RequestMapping(value = "/employees/{id}")
public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
{
    if (id <= 3) {
        EmployeeVO employee = EmployeeDB.getEmployeeList().get(id-1);
        
        //Self link
        Link selfLink = ControllerLinkBuilder
        		.linkTo(EmployeeRESTController.class)
        		.slash(employee.getEmployeeId())
        		.withSelfRel();
        
        //Method link
        Link reportLink = ControllerLinkBuilder
        		.linkTo(ControllerLinkBuilder.methodOn(EmployeeRESTController.class)
        		.getReportByEmployeeById(employee.getEmployeeId()))
        		.withRel("report");
        
        employee.add(selfLink);
        employee.add(reportLink);
        return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
    }
    return new ResponseEntity<EmployeeVO>(HttpStatus.NOT_FOUND);
}

Output:

{
    "employeeId": 1,
    "firstName": "Lokesh",
    "lastName": "Gupta",
    "email": "howtodoinjava@gmail.com",
    "_links": {
        "self": {
            "href": "http://localhost:8080/1"
        },
        "report": {
            "href": "http://localhost:8080/employees/1/report"
        }
    }
}

Summary

As you saw the demo above that adding HATEOAS links using spring hateoas module is very much easy and one time effort. It will greatly increase the discoverability and usefulness of APIs by many folds.

Drop me your questions in comments section.

Happy Learning !!

Download Sourcecode

Was this post helpful?

Let us know if you liked the post. That’s the only way we can improve.
TwitterFacebookLinkedInRedditPocket

About Lokesh Gupta

A family guy with fun loving nature. Love computers, programming and solving everyday problems. Find me on Facebook and Twitter.

Feedback, Discussion and Comments

  1. Zsolt

    February 25, 2020

    public class EmployeeVO extends ResourceSupport implements Serializable

    extends ResourceSupport: this part is missing from the code snippet here

  2. Sreenivasan Rajagopalan

    May 11, 2018

    Good Post!

    But what if I have to add to Employee Resource a link to the Department Resource which is in a different domain – like the link of Department is “http://localhost:8181/departments/1” .. Can I build a link to that from within the Employee module if Department Id is an attribute of Employee ?

    Thanks,

    -Sreeni

Comments are closed on this article!

Search Tutorials

Spring Boot REST

  • SB REST – Hello World
  • SB REST – @RestController
  • SB REST – JSON
  • SB REST – POST with Headers
  • SB REST – HATEOAS
  • SB REST – @Async
  • SB REST – SseEmitter
  • SB REST – ResponseBodyEmitter
  • SB REST – Callable
  • SB REST – Unit Testing
  • SB REST – Gzip Compression
  • SB REST – i18n

Spring Boot 2 Tutorial

  • Spring Boot – Introduction
  • Spring Boot – Starter parent
  • Spring Boot – Starter templates
  • Spring Boot – Multi-module project
  • Spring Boot – Annotations
  • Spring Boot – Auto configuration
  • Spring Boot – AOP
  • Spring Boot – Logging
  • Spring Boot – DevTools
  • Spring Boot – WAR Packaging
  • Spring Boot – REST API
  • Spring Boot – CRUD
  • Spring Boot – OAuth2
  • Spring Boot – Testing
  • Spring Boot – RestTemplate
  • Spring Boot – Thymeleaf
  • Spring Boot – Hibernate
  • Spring Boot – DataSource
  • Spring Boot – Error Handling
  • Spring Boot – Caching
  • Spring Boot – Retry
  • Spring Boot – BasicAuth
  • Spring Boot – H2 Database
  • Spring Boot – Ehcache 3.x
  • Spring Boot – Gson
  • Spring Boot – RMI
  • Spring Boot – Send Email
  • Spring Boot – Interview Questions

Spring Boot Tutorial

  • Spring Boot – CommandLineRunner
  • Spring Boot – Configure Jetty
  • Spring Boot – Tomcat Default Port
  • Spring Boot – Context Root
  • Spring Boot – SSL [https]
  • Spring Boot – Get all loaded beans
  • Spring Boot – PropertyEditor
  • Spring Boot – @EnableScheduling
  • Spring Boot – Jersey
  • Spring Boot – SOAP Webservice
  • Spring Boot – SOAP Client
  • Spring Boot – JMSTemplate
  • Spring Boot – REST APIs
  • Spring Boot – JSP View
  • Spring Boot – Actuator endpoints
  • Spring Boot – Role Based Security
  • Spring Boot – RSS / ATOM Feed
  • Spring Boot – Ehcache 2.x

Meta Links

  • About Me
  • Contact Us
  • Privacy policy
  • Advertise
  • Guest and Sponsored Posts

Recommended Reading

  • 10 Life Lessons
  • Secure Hash Algorithms
  • How Web Servers work?
  • How Java I/O Works Internally?
  • Best Way to Learn Java
  • Java Best Practices Guide
  • Microservices Tutorial
  • REST API Tutorial
  • How to Start New Blog

Copyright © 2020 · HowToDoInjava.com · All Rights Reserved. | Sitemap

  • Sealed Classes and Interfaces