Building Your First REST API with Spring Boot
This post links to: Building a Spring Boot CRUD App With Postgres from Scratch: The Complete Guide.
This guide follows on from Creating Your First JPA Entity and Repository in Spring Boot. Detailed below is how to create a REST Controller that will interact with our JPA repository.
REST Controllers & APIs
To understand REST controllers and REST APIs, you will want to understand what is RESTful API architecture?
TL;DR: REST is an architectural style for designing APIs (Application Programming Interfaces). APIs in general are interfaces with defined rules between services to allow communication and transfer of data.
In the context of backend webservices, APIs are an entrypoint to access the data from a backend service. The API defines what data you can ask for and how you ask for it.
The MVC (Model-View-Controller) pattern is commonly used to design and develop web applications.
Model: Handles data and business logic, backend services and databasesView: Handles what the user sees, a frontend application/UIController: Handles requests and connects the View + Model, such as a REST API
In this guide, we will be focusing on the Model and Controller parts of the MVC pattern.
The basic structure of the layered app will be:Controller → Service → Repository → Database
Controller: handles the HTTP requests and responsesService: Handles our business logicRepository: Communicates with our database
Creating the User Service
We can start by creating a simple user service that separates the database logic from the controller logic.
This service will be able to:
- Get all users
- Get a user by their ID
- Create a user
- Update a user
- Delete a user
// UserService.java
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUserById(final Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found with ID: " + id));
}
public User createUser(final User user) {
return userRepository.save(user);
}
public User updateUser(final Long id, final User updatedUser) {
final var user = getUserById(id);
user.setName(updatedUser.getName());
user.setEmail(updatedUser.getEmail());
return userRepository.save(user);
}
public void deleteUser(final Long id) {
userRepository.deleteById(id);
}
}
The @Service annotation will tell Spring to automatically detect this class during component scanning and to create and manage an instance of UserService on start up.
This means you don't need to do final var userService = new UserService(), Spring handles this for you.
@Service
public class UserService {
// ...
}
The @Service annotation is a specialised version of @Component. It provides semantic meaning that says “this class contains service/business logic”
You can also see an example of constructor-based dependency injection:
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
// ...
}
The UserService has a constructor that expects an instance of UserRepository to be passed in.
Spring will detect this, try to find an instance of UserRepository that has already been created and pass it in.
Constructor-based dependency injection is recommended in modern Spring. It allows for loose coupling, easier testing, separation of concerns, reusability and cleaner code.
Spring ensures you only have one managed instance created, rather than multiple unmanaged instances.
Creating a Spring REST Controller
Next, we will create a Spring REST controller to perform CRUD actions on our data.
CRUD is an acronym for the four fundamental operations on data in a database - Create, Read, Update and Delete.
These operations translate into the following HTTP request methods POST (Create), GET (Read), PUT (Update) and DELETE (Delete).
You can view a full list of HTTP request methods here.
// UserController.java
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(final UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable("id") final Long id) {
return userService.getUserById(id);
}
@PostMapping
public User createUser(@RequestBody final User user) {
return userService.createUser(user);
}
@PutMapping("/{id}")
public User updateUser(@PathVariable("id") final Long id, @RequestBody final User updatedUser) {
return userService.updateUser(id, updatedUser);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable("id") final Long id) {
userService.deleteUser(id);
}
}
Let's break down some of these annotations:
@RestController- This tells Spring this is a REST API and to create aUserControllerbean. It will also tell Spring to automatically serialize values using Jackson to return JSON responses.@RequestMapping- Sets the base URL path for the controller to/api/users. It ensures that any endpoint in this controller will always have a prefix of/api/users.@GetMapping, etc. - Maps HTTP methods to controller methods. They are shorthand for@RequestMapping(method = RequestMethod.METHOD_NAME).@RequestBody- Converts (deserialises) JSON in the request body into a Java object.@PathVariable- Binds a dynamic part of the URL to a method parameter extracting whatever value is at the placeholder.
Testing Our REST Controller
Now we have our UserService and UserController set up we can test whether it works or not.
For testing, we're going to use cURL in the terminal.
We can start by creating a user:
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Jon","email": "jon@example.com"}'
Then we can either get all the stored users:
curl -X GET http://localhost:8080/api/users
Or get a single user by their ID:
curl -X GET http://localhost:8080/api/users/1
We can then update our user:
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"name": "JonUpdated", "email": "jon-updated@example.com"}'
See that our user was updated:
curl -X GET http://localhost:8080/api/users/1
And finally, delete our user:
curl -X DELETE http://localhost:8080/api/users/1
What's Next?
So to recap we:
- Learnt about REST, CRUD and MVC
- Created a user service to deal with business logic
- Created a user controller to access our user data via a REST API
- Learnt about the annotations in Spring that help build our application
Up Next: DTOs and Clean API Design