Spring Boot Microservices REST API Example Tutorial

In this tutorial, you will learn how to build two Spring Boot microservices with REST APIs, perform CRUD operations, and communicate between services. We will create employee-service and department-service, where the employee-service performs CRUD operations on employee data, and department-service communicates with employee-service to retrieve employee details using FeignClient.

What You’ll Learn:

  • Building CRUD REST APIs with Spring Boot.
  • Communicating between two microservices using REST.
  • Using Java 21 and Jakarta EE for entity management.
  • Following constructor-based dependency injection (best practice).

Introduction to Spring Boot REST APIs

In a microservices architecture, each service exposes its own REST API. Each microservice has its own database and logic, and services communicate via HTTP, ensuring loose coupling and scalability.

Prerequisites

Before starting, ensure that you have the following tools installed:

  • JDK 21 or later
  • Maven (to build the project)
  • MySQL or an in-memory database like H2
  • Postman or curl (to test the REST APIs)
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Create the Projects

We will create two microservices:

  1. employee-service: Manages CRUD operations for employee data.
  2. department-service: Communicates with employee-service to retrieve employee data.

Step 2: Set Up employee-service

2.1 Create the Project

Go to Spring Initializr and generate a Spring Boot project with the following dependencies:

  • Spring Web
  • Spring Data JPA
  • MySQL Driver (or H2 Database for simplicity)
  • Lombok

Make sure to select the latest Java version: Java 21.

2.2 Configure application.properties

In the src/main/resources/application.properties file, configure the MySQL or H2 database for employee-service.

For MySQL:

spring.datasource.url=jdbc:mysql://localhost:3306/employees_db
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
server.port=8081

Update the above database connection properties as per your MySQL set up.

For H2:

spring.datasource.url=jdbc:h2:mem:employees_db
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
server.port=8081

2.3 Create the Employee Entity

Define the Employee entity with the following content:

package com.example.employeeservice.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String department;
}

Explanation:

  • jakarta.persistence replaces javax.persistence (no deprecated javax).
  • Lombok is used for boilerplate code like getters, setters, constructors, etc.

2.4 Create Employee Repository

Create a JpaRepository for the Employee entity.

package com.example.employeeservice.repository;

import com.example.employeeservice.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;

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

2.5 Create Employee Service

Create a service class to handle business logic for CRUD operations using constructor-based dependency injection (best practice).

package com.example.employeeservice.service;

import com.example.employeeservice.entity.Employee;
import com.example.employeeservice.repository.EmployeeRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public EmployeeService(EmployeeRepository employeeRepository) {
        this.employeeRepository = employeeRepository;
    }

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

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

    public Optional<Employee> getEmployeeById(Long id) {
        return employeeRepository.findById(id);
    }

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

Explanation:

  • Constructor-based dependency injection is used for EmployeeRepository in EmployeeService.

2.6 Create REST Controller

Create a REST controller to expose the CRUD REST APIs and Use constructor-based dependency injection to inject EmployeeService.

import com.example.employeeservice.entity.Employee;
import com.example.employeeservice.service.EmployeeService;
import org.springframework.web.bind.annotation.*;

import java.util.List;

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

    private final EmployeeService employeeService;

    public EmployeeController(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    @PostMapping
    public Employee addEmployee(@RequestBody Employee employee) {
        return employeeService.saveEmployee(employee);
    }

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

    @GetMapping("/{id}")
    public Employee getEmployeeById(@PathVariable Long id) {
        return employeeService.getEmployeeById(id).orElse(null);
    }

    @DeleteMapping("/{id}")
    public String deleteEmployee(@PathVariable Long id) {
        employeeService.deleteEmployee(id);
        return "Employee deleted with id: " + id;
    }
}

Explanation:

  • REST endpoints for creating, retrieving, and deleting employees are exposed.

2.7 Create a Dockerfile

Create a Dockerfile for employee-service:

FROM openjdk:21-jdk-alpine
WORKDIR /app
COPY target/employee-service-0.0.1-SNAPSHOT.jar employee-service.jar
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "employee-service.jar"]

Step 3: Set Up department-service

3.1 Create the Project

Go to Spring Initializr and generate another Spring Boot project with the following dependencies:

  • Spring Web
  • Spring Boot Actuator
  • OpenFeign (for inter-service communication)
Make sure to select the latest Java version: Java 21.

3.2 Configure application.properties

Configure the department-service to communicate with employee-service.

server.port=8082
spring.application.name=department-service
employee.service.url=http://localhost:8081/employees

Explanation:

  • employee.service.url: Defines the URL for communicating with employee-service.

3.3 Create Feign Client

Create a Feign client to communicate with employee-service.

package com.example.departmentservice.client;

import com.example.departmentservice.model.Employee;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.List;

@FeignClient(name = "employee-service", url = "${employee.service.url}")
public interface EmployeeClient {

    @GetMapping
    List<Employee> getAllEmployees();

    @GetMapping("/{id}")
    Employee getEmployeeById(@PathVariable Long id);
}

3.4 Create Employee Model

Create an Employee model in department-service to match the structure in employee-service.

package com.example.departmentservice.model;

public class Employee {
    private Long id;
    private String name;
    private String department;

    // Constructors, getters, and setters
}

3.5 Create Department Service

Create the DepartmentService to handle the logic for retrieving employee data from employee-service.

package com.example.departmentservice.service;

import com.example.departmentservice.client.EmployeeClient;
import com.example.departmentservice.model.Employee;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DepartmentService {

    private final EmployeeClient employeeClient;

    public DepartmentService(EmployeeClient employeeClient) {
        this.employeeClient = employeeClient;
    }

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

    public Employee getEmployeeById(Long id) {
        return employeeClient.getEmployeeById(id);
    }
}

3.6 Create REST Controller

Create a REST controller in department-service to fetch employee details from employee-service.

package com.example.departmentservice.controller;

import com.example.departmentservice.model.Employee;
import com.example.departmentservice.service.DepartmentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/departments")
public class DepartmentController {

    private final DepartmentService departmentService;

    public DepartmentController(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }

    @GetMapping("/employees")
    public List<Employee> getAllEmployees() {
        return departmentService.getAllEmployees();
    }

    @GetMapping("/employees/{id}")
    public Employee getEmployeeById(@PathVariable Long id) {
        return departmentService.getEmployeeById(id);
    }
}

3.7 Create a Dockerfile

Create a Dockerfile for department-service:

FROM openjdk:21-jdk-alpine
WORKDIR /app
COPY target/department-service-0.0.1-SNAPSHOT.jar department-service.jar
EXPOSE 8082
ENTRYPOINT ["java", "-jar", "department-service.jar"]

Step 4: Build Docker Images

Navigate to the root directories of each service and run the following commands to build Docker images:

For employee-service:

mvn clean package
docker build -t employee-service .

For department-service:

mvn clean package
docker build -t department-service .

Step 5: Set Up Docker Compose

Create a docker-compose.yml file to run both microservices:

version: '3.8'

services:
  employee-service:
    image: employee-service
    build:
      context: ./employee-service
    ports:
      - "8081:8081"
    networks:
      - microservices-net

  department-service:
    image: department-service
    build:
      context: ./department-service
    ports:
      - "8082:8082"
    networks:
      - microservices-net

networks:
  microservices-net:
    driver: bridge

Step 6: Run Docker Compose

Navigate to the directory containing the docker-compose.yml file and run the following command:

docker-compose up --build

This will build and start both microservices.

Step 7: Test the REST Endpoints

You can use Postman or curl to test the endpoints.

  1. Create an Employee:
curl -X POST http://localhost:8081/employees \
-H "Content-Type: application/json" \
-d '{"name":"John Doe", "department":"Engineering"}'
  1. Get All Employees (from department-service):
curl http://localhost:8082/departments/employees
  1. Get Employee by ID (from department-service):
curl http://localhost:8082/departments/employees/1

These endpoints will allow you to test CRUD operations and inter-service communication.

Conclusion

You’ve successfully built two Spring Boot microservices with REST APIs using Java 21 and Jakarta EE. The services use constructor-based dependency injection, and department-service communicates with employee-service using FeignClient.

Comments