Let's put what we've learned into practice and build a REST API to work with entities.
Step-by-step plan to create a REST API
In this lecture we'll create a REST API to manage "User" entities. We'll start by creating a Spring Boot project and implement a CRUD (Create, Read, Update, Delete) API step by step.
1. Creating a Spring Boot project
First, we'll need a new Spring Boot project.
Use Spring Initializr:
- Go to Spring Initializr.
- Set the following options:
- Project: Maven
- Language: Java
- Spring Boot: 2.x.x (or newer)
- Dependencies: Spring Web, Spring Data JPA, H2 Database (or another), Lombok (optional but handy).
- Download and unzip the project.
2. Configuring application.properties
First, configure the database. In src/main/resources/application.properties set the connection parameters for H2 Database:
# H2 Database configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
We're using H2 — an embedded database for simplicity during development. If you want to use another database, like PostgreSQL, you'll need to make the appropriate changes.
3. Creating the User entity
Time to get to work. First, define the User entity which will represent a user in our system.
package com.example.demo.entity;
import jakarta.persistence.*;
import lombok.Data;
@Data // Lombok annotation to auto-generate getters/setters
@Entity // Marks the class as a JPA entity
@Table(name = "users") // Specifies the table name
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-generate ID
private Long id;
@Column(nullable = false) // Field cannot be null
private String name;
@Column(nullable = false, unique = true) // Unique email
private String email;
@Column(nullable = false)
private Integer age;
}
4. Creating the JPA repository
Now create a repository to interact with the database.
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
// JpaRepository provides ready-to-use methods for DB operations
public interface UserRepository extends JpaRepository<User, Long> {
}
5. Creating the REST controller
Now for the fun part — implementing the REST API! Let's create a controller that will handle CRUD operations for User entities.
Example code:
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController // Marks the class as a REST controller
@RequestMapping("/api/users") // Base path for all endpoints
public class UserController {
private final UserRepository userRepository;
// Inject dependency via constructor
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
// Get all users (GET /api/users)
@GetMapping
public List<User> getAllUsers() {
return userRepository.findAll();
}
// Get a single user by ID (GET /api/users/{id})
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userRepository.findById(id)
.map(ResponseEntity::ok) // If user is found
.orElse(ResponseEntity.notFound().build()); // If not found
}
// Create a new user (POST /api/users)
@PostMapping
public User createUser(@RequestBody User user) {
return userRepository.save(user);
}
// Update an existing user (PUT /api/users/{id})
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User updatedUser) {
return userRepository.findById(id)
.map(user -> {
user.setName(updatedUser.getName());
user.setEmail(updatedUser.getEmail());
user.setAge(updatedUser.getAge());
return ResponseEntity.ok(userRepository.save(user));
})
.orElse(ResponseEntity.notFound().build());
}
// Delete a user (DELETE /api/users/{id})
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
return userRepository.findById(id)
.map(user -> {
userRepository.delete(user);
return ResponseEntity.noContent().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
ResponseEntity so the client can get the correct HTTP status code (for example, 404 if the user isn't found).
6. Testing the API
Now let's test our API. You can use a tool like Postman to send requests.
Request examples:
- GET /api/users — get all users.
- POST /api/users — create a user. Example request body:
{ "name": "Ivan Ivanov", "email": "ivan@example.com", "age": 25 } - GET /api/users/1 — get the user with ID = 1.
- PUT /api/users/1 — update a user. Example request body:
{ "name": "Ivan Petrov", "email": "petrov@example.com", "age": 30 } - DELETE /api/users/1 — delete the user with ID = 1.
7. Possible issues and how to fix them
Issue: the email field isn't unique
If you try to add two users with the same email, you'll get a DB error. To prevent this, add a data pre-check (for example, check if the email already exists before creating the user).
Issue: validation
Right now the data isn't validated. For example, you can create a user with an empty name. We'll fix this later using the @Valid annotation.
Issue: serialization errors
If an object contains references to other entities with cyclic dependencies, Jackson might throw an error. Fix: use the @JsonIgnore annotation (we'll come back to this later).
8. Applying this in real projects
The REST API we built in this lecture is the foundation of any modern application. You can use this approach to manage entities like products, orders, employees, and more. These skills are essential for working as a Java developer, especially in web apps and microservices.
Now you know how to create a REST API from scratch. In the next lecture we'll look at how to design URIs properly and work with resources so your API becomes convenient and professional.
GO TO FULL VERSION