How to choose between Spring Boot Web applications and REST APIs
Problem
When I started building Spring Boot applications, I got confused about when to use @Controller vs @RestController. I kept mixing them up and building APIs that returned HTML instead of JSON.
// WRONG: This returns HTML when I wanted JSON@Controllerpublic class UserController { @GetMapping("/api/users") public List<User> getAllUsers() { return userService.getAllUsers(); }}When clients called my /api/users endpoint, they got HTML pages instead of JSON data. The clients couldn’t parse the response and everything broke.
Environment
- Spring Boot 3.2
- Java 21
- Maven 3.9
- Thymeleaf 3.1
What happened?
I was building a web application that needed to serve both user-facing HTML pages and machine-readable JSON APIs. I didn’t understand the difference between web applications and REST APIs in Spring Boot.
Here’s my setup:
@SpringBootApplicationpublic class WebAppApplication { public static void main(String[] args) { SpringApplication.run(WebAppApplication.class, args); }}I can explain the key parts:
@SpringBootApplication: Enables auto-configurationmain: Application entry point
But when I tried to create both web pages and APIs in the same controller, I got mixed results:
@Controllerpublic class MixedController {
// This works - returns HTML page @GetMapping("/home") public String home(Model model) { model.addAttribute("message", "Welcome!"); return "home"; // Resolves to home.html }
// This doesn't work as expected - returns HTML instead of JSON @GetMapping("/api/users") public List<User> getUsers() { return userService.getAllUsers(); }}When I called /api/users, I got HTML instead of JSON. The client applications couldn’t handle this response format.
How to solve it?
I tried to fix this by adding @ResponseBody to the API endpoint:
// Adding @ResponseBody to return JSON@GetMapping("/api/users")@ResponseBodypublic List<User> getUsers() { return userService.getAllUsers();}What changed and why: The @ResponseBody annotation tells Spring to serialize the return object directly to the HTTP response body instead of looking for a template. But this is messy - I’m mixing annotations.
Then I found the proper solution - use @RestController for APIs:
@RestController@RequestMapping("/api/users")public class UserController {
@GetMapping public List<User> getAllUsers() { return userService.getAllUsers(); }
@GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { return userService.findById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); }}And keep web controllers separate:
@Controllerpublic class WebController {
@GetMapping("/home") public String home(Model model) { model.addAttribute("message", "Welcome to our website!"); return "home"; // Resolves to home.html template }}Now test again:
# For web pagecurl -I http://localhost:8080/homeHTTP/1.1 200Content-Type: text/html;charset=UTF-8
# For APIcurl -I http://localhost:8080/api/usersHTTP/1.1 200Content-Type: application/jsonYou can see that I succeeded to serve both HTML pages and JSON data from the same application.
The reason
I think the key reason for the confusion is that @RestController is actually a combination of @Controller and @ResponseBody:
// @RestController annotation definition@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Controller@ResponseBodypublic @interface RestController { String value() default "";}So:
@Controller: Returns View objects that resolve to templates@RestController: Returns data objects directly as JSON/XML@Controller+@ResponseBody: Returns data objects but allows more configuration
The web application approach works because Spring uses the ViewResolver to find templates. The REST API approach works because Spring uses HttpMessageConverter to serialize objects directly.
Summary
In this post, I showed how to choose between web applications and REST APIs in Spring Boot. The key point is using @Controller with template engines for user-facing interfaces and @RestController for machine-readable data services. Many modern applications benefit from a hybrid approach with separate controllers for web and API functionality.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments