100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Java

Building REST Controllers

Learn how to build REST controllers in Spring Boot using @RestController, structure methods cleanly, and return well-formed HTTP responses.

Web LayerBeginner9 min readJul 10, 2026
Analogies

What Is a REST Controller in Spring Boot?

A REST controller is a Spring-managed class that exposes HTTP endpoints and returns data (usually JSON) rather than a view name. Spring Boot provides the @RestController annotation, which is a convenience annotation combining @Controller and @ResponseBody, so every method's return value is automatically serialized into the HTTP response body instead of being resolved to a template.

🏏

Cricket analogy: Think of a REST controller like the third umpire's review station: a request comes in (the on-field appeal), the controller processes it against the rules, and sends back a clear decision (JSON verdict) rather than a vague gesture.

@RestController vs @Controller

@Controller alone is meant for MVC applications where methods typically return a view name resolved by a ViewResolver (e.g., a Thymeleaf template). If you need a mix of view-returning and JSON-returning methods in the same class, you can keep @Controller and annotate individual methods with @ResponseBody. For pure APIs, @RestController is the standard choice because it applies @ResponseBody to every handler method automatically, removing repetitive annotations.

🏏

Cricket analogy: It's like a stadium announcer who mostly narrates the match for the crowd (@Controller returning views) but occasionally reads out a raw scorecard number for the broadcast feed (@ResponseBody) — @RestController is the dedicated scoreboard operator who only ever outputs raw numbers.

java
@RestController
@RequestMapping("/api/products")
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping
    public List<ProductDto> listProducts() {
        return productService.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<ProductDto> getProduct(@PathVariable Long id) {
        ProductDto product = productService.findById(id);
        return ResponseEntity.ok(product);
    }
}

Keeping Controllers Thin

A well-built controller should focus purely on HTTP concerns: mapping routes, extracting parameters, and translating results into responses. Business logic, transaction management, and data access belong in a service layer that the controller delegates to via constructor injection. This separation keeps controllers testable with lightweight mocks and prevents HTTP-layer classes from becoming tangled with persistence or domain rules.

🏏

Cricket analogy: A good captain like Rohit Sharma sets the field and calls the shots but doesn't bowl every over himself — the controller sets up the request and delegates the real work (bowling) to specialist service classes.

Keep controller methods short. If a handler method exceeds roughly 15-20 lines or contains conditional business rules, that's usually a sign the logic belongs in a service class instead.

Returning Responses with ResponseEntity

Returning a plain object (like the List<ProductDto> above) always results in a 200 OK status with the serialized body. When you need control over the status code, headers, or to signal 201 Created, 404 Not Found, or 204 No Content, wrap the return value in a ResponseEntity<T>. This makes the controller's contract with API consumers explicit rather than relying on default status behavior.

🏏

Cricket analogy: It's like the umpire choosing between signaling a plain single (default 200 OK) versus explicitly raising both arms for a six (201 Created) — ResponseEntity lets you pick the exact signal.

Never return JPA entities directly from a controller. Lazy-loaded associations can trigger LazyInitializationException outside the persistence context, and entities often expose internal fields you don't want in your public API. Map to a dedicated DTO instead.

  • @RestController combines @Controller and @ResponseBody so every method's return value is serialized directly into the response body.
  • Use @Controller with per-method @ResponseBody only when a class mixes view-rendering and JSON-returning methods.
  • Constructor injection of a service layer keeps controllers thin and focused on HTTP concerns only.
  • Business logic, validation rules, and persistence access belong in service and repository layers, not controllers.
  • ResponseEntity<T> gives explicit control over status codes, headers, and the response body.
  • Never expose JPA entities directly; always map to DTOs to avoid lazy-loading errors and leaking internal fields.
  • Route mapping annotations like @GetMapping and @RequestMapping define the URL surface a controller class exposes.

Practice what you learned

Was this page helpful?

Topics covered

#Java#SpringBootStudyNotes#WebDevelopment#BuildingRESTControllers#Building#REST#Controllers#Controller#APIs#StudyNotes#SkillVeris