Open In App

Spring Boot – Building REST APIs with HATEOAS

Last Updated : 22 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In this article, we will explore how to build RESTful APIs using the Spring Boot with HATEOAS (Hypermedia as the Engine of Application State). HATEOAS is the key component of the REST application architecture, where each resource not only provides the data but also includes links to other actions that can be performed on the resource. This approach allows the clients to navigate through the application using these links, making the API more dynamic and discoverable.

HATEOAS in Spring Boot

HATEOAS is the concept within REST that enables the client to interact with the RESTful service entirely through hyperlinks provided dynamically by the service itself. It means the client does not need to hardcode the URIs of the resource but can discover them at runtime.

In Spring Boot, HATEOAS can be implemented using the spring-boot-starter-hateoas dependency which allows the easy creation of hypermedia links and resources. With HATEOAS each resource representation returned by the server can contain the links to related resources or actions that the client can follow.

Implementation to Build REST APIs with HATEOAS in Spring Boot

Step 1: Create a new Spring Boot Project.

Create a new Spring Boot project using IntelliJ Idea. Choose the below options:

  • Name: Spring-HATEOAS-Demo
  • Language: Java
  • Type: Maven
  • Packaging: Jar

Click on the Next button.

project metadata

Step 2: Add the Dependencies

Add the following dependencies into the Spring Boot project. click on the create button to create the project.

Add the Dependencies

Step 3: Project Structure

Once created the project, the file structure looks like:

Project Structure

Step 4: Configure Application Properties

Open the application.properties file and add the following configuration:

spring.application.name=Spring-HATEOAS-Demo

spring.datasource.url=jdbc:mysql://localhost:3306/employee_db
spring.datasource.username=root
spring.datasource.password=mypassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

Step 5: Create the Employee Entity

Create the Employee Entity class represents the database table structure of the Spring Boot project.

Java
package com.gfg.springhateoasdemo;


import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String role;

    // Constructors
    public Employee() {}

    public Employee(String name, String role) {
        this.name = name;
        this.role = role;
    }

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }
}

Step 6: Create the EmployeeRepository Interface

Now create the EmployeeRepository class can be extends JpaRepository to provide the CRUD operations.

Java
package com.gfg.springhateoasdemo;

import org.springframework.data.jpa.repository.JpaRepository;


public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}

Step 7: Create the EmployeeService Class

Now create the EmployeeService class contains the business logic for the CRUD operations of the project.

Java
package com.gfg.springhateoasdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    public Employee createEmployee(Employee employee) {
        return employeeRepository.save(employee);
    }

    public Employee getEmployeeById(Long id) {
        return employeeRepository.findById(id).orElse(null);
    }

    public List<Employee> getAllEmployees() {
        return employeeRepository.findAll();
    }

    public Employee updateEmployee(Long id, Employee employeeDetails) {
        Employee employee = getEmployeeById(id);
        if (employee != null) {
            employee.setName(employeeDetails.getName());
            employee.setRole(employeeDetails.getRole());
            return employeeRepository.save(employee);
        }
        return null;
    }

    public void deleteEmployee(Long id) {
        employeeRepository.deleteById(id);
    }
}

Step 8: Create the EmployeeController Class

Create the EmployeeController class handles the HTTP requests and adds the HATEOAS links of the application endpoints.

Java
package com.gfg.springhateoasdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.web.bind.annotation.*;


import java.util.List;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;

@RestController
@RequestMapping("/employees")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @PostMapping
    public EntityModel<Employee> createEmployee(@RequestBody Employee employee) {
        Employee createdEmployee = employeeService.createEmployee(employee);
        return toHateoasEntityModel(createdEmployee);
    }

    @GetMapping("/{id}")
    public EntityModel<Employee> getEmployee(@PathVariable Long id) {
        Employee employee = employeeService.getEmployeeById(id);
        return toHateoasEntityModel(employee);
    }

    @GetMapping
    public List<Employee> getAllEmployees() {
        return employeeService.getAllEmployees();
    }

    @PutMapping("/{id}")
    public EntityModel<Employee> updateEmployee(@PathVariable Long id, @RequestBody Employee employeeDetails) {
        Employee updatedEmployee = employeeService.updateEmployee(id, employeeDetails);
        return toHateoasEntityModel(updatedEmployee);
    }

    @DeleteMapping("/{id}")
    public void deleteEmployee(@PathVariable Long id) {
        employeeService.deleteEmployee(id);
    }

    private EntityModel<Employee> toHateoasEntityModel(Employee employee) {
        Link selfLink = linkTo(methodOn(EmployeeController.class).getEmployee(employee.getId())).withSelfRel();
        Link allEmployeesLink = linkTo(methodOn(EmployeeController.class).getAllEmployees()).withRel("all-employees");

        return EntityModel.of(employee, selfLink, allEmployeesLink);
    }
}

Step 9: Main Class

No changes are required in the main class.

Java
package com.gfg.springhateoasdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HateoasApplication {

    public static void main(String[] args) {
        SpringApplication.run(HateoasApplication.class, args);
    }

}

pom.xml file

XML
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gfg</groupId>
    <artifactId>Spring-HATEOAS-Demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Spring-HATEOAS-Demo</name>
    <description>Spring-HATEOAS-Demo</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Step 10: Run the Application

Once completed the project, it will start and run at port 8080.

Run the Application

Step 11: Testing the Endpoints

1. Create Employee

POST http://localhost:8080/employees
postman output ui

2. Get All Employees

GET  http://localhost:8080/employees
postman output ui

3. Get Employee by ID

GET http://localhost:8080/employees/1
postman output ui

4. Update Employee

PUT http://localhost:8080/employees/1
postman output ui

5. Delete Employee

DELETE http://localhost:8080/employees/1
postman output ui

By following these steps, we can test each of the CRUD operations for the Spring Boot application with HATEOAS and MySQL. Each request should be conform to the structure and links defined in the example project.


Next Article

Similar Reads