Today we'll walk through a hands-on build of a microservice that manages the Customer entity. We'll implement CRUD operations, hook up a database via Spring Data JPA, and test everything with tools like Postman.
Step 1: prepare the environment and create a new project
First, create a new project. You can do it via Spring Initializr:
- Set the following options:
- Project: Maven
- Language: Java
- Spring Boot Version: 3.0.x+
- Dependencies:
- Spring Web
- Spring Data JPA
- H2 Database (embedded database for testing)
- Spring Boot DevTools (for easier development)
- Download the project and open it in your IDE (for example, IntelliJ IDEA).
Step 2: database configuration
Create the application.yml file in src/main/resources (if it's missing). Configure the H2 connection like this:
spring:
datasource:
url: jdbc:h2:mem:customerdb
driver-class-name: org.h2.Driver
username: sa
password: password
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
This file configures an in-memory (H2) database and tells Hibernate to automatically update the table schema. The show-sql option lets you see SQL queries in the console, which is super useful for debugging.
Step 3: create the Customer entity
Add the Customer class to package com.example.customer:
package com.example.customer;
import jakarta.persistence.*;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // Automatic ID generation
private Long id;
@Column(nullable = false) // Field can't be null
private String name;
@Column(nullable = false, unique = true)
private String email;
public Customer() {
// Default constructor for JPA
}
public Customer(String name, String email) {
this.name = name;
this.email = email;
}
// 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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
We defined the Customer entity with fields id, name, and email. The @Entity annotation tells JPA that this class maps to a table in the database.
Step 4: create the repository
Create the CustomerRepository interface:
package com.example.customer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
// JpaRepository provides basic CRUD operations
}
This interface makes it easy to work with the DB without writing SQL. For example, methods like save(), findById(), findAll(), and deleteById() are available right after you add the repository.
Step 5: create the service
Add the CustomerService class:
package com.example.customer;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class CustomerService {
private final CustomerRepository customerRepository;
public CustomerService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public List<Customer> getAllCustomers() {
return customerRepository.findAll();
}
public Customer getCustomerById(Long id) {
return customerRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Customer not found with id: " + id));
}
public Customer createCustomer(Customer customer) {
return customerRepository.save(customer);
}
public Customer updateCustomer(Long id, Customer updatedCustomer) {
Customer existingCustomer = getCustomerById(id);
existingCustomer.setName(updatedCustomer.getName());
existingCustomer.setEmail(updatedCustomer.getEmail());
return customerRepository.save(existingCustomer);
}
public void deleteCustomer(Long id) {
customerRepository.deleteById(id);
}
}
This class encapsulates the business logic for working with customers.
Step 6: create the REST controller
Add the CustomerController class:
package com.example.customer;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/customers")
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
@GetMapping
public List<Customer> getAllCustomers() {
return customerService.getAllCustomers();
}
@GetMapping("/{id}")
public ResponseEntity<Customer> getCustomerById(@PathVariable Long id) {
try {
Customer customer = customerService.getCustomerById(id);
return ResponseEntity.ok(customer);
} catch (IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Customer createCustomer(@RequestBody Customer customer) {
return customerService.createCustomer(customer);
}
@PutMapping("/{id}")
public ResponseEntity<Customer> updateCustomer(@PathVariable Long id, @RequestBody Customer updatedCustomer) {
try {
Customer customer = customerService.updateCustomer(id, updatedCustomer);
return ResponseEntity.ok(customer);
} catch (IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id);
}
}
The controller handles HTTP requests and calls the service methods. Here we use @RestController, @GetMapping, @PostMapping, @PutMapping, and @DeleteMapping annotations to route requests.
Step 7: testing the REST API
Run the app: click Run in your IDE or run mvn spring-boot:run in the terminal.
Testing endpoints via Postman
- Get all customers (GET):
- URL:
http://localhost:8080/customers - Response:
[ ](empty list by default).
- URL:
- Create a new customer (POST):
- URL:
http://localhost:8080/customers - Body (JSON):
{ "name": "John Doe", "email": "john.doe@example.com" } - Response: the created object.
- URL:
- Get customer by ID (GET):
- URL:
http://localhost:8080/customers/1.
- URL:
- Update a customer (PUT):
- URL:
http://localhost:8080/customers/1 - Body (JSON):
{ "name": "Jane Doe", "email": "jane.doe@example.com" }
- URL:
- Delete a customer (DELETE):
- URL:
http://localhost:8080/customers/1.
- URL:
Step 8: error handling and improvements
If you try to delete or update a non-existent customer, the app should return 404. That's already handled in our controller using ResponseEntity and custom exceptions.
Congrats! You just built your first microservice with a REST API. Now you're ready to build more and scale!
GO TO FULL VERSION