# REST Controllers — Spring Boot

Source: https://www.skillbyai.com/en/spring-boot/w-controller

> Methods return data; Jackson writes JSON.

## @RestController and DTO records

A `@RestController` class maps HTTP requests to methods with `@GetMapping`, `@PostMapping` and friends; return values are converted to JSON by Jackson. Use small **records as DTOs** (request and response shapes) rather than exposing JPA entities directly, so the API stays stable when the database model changes. `ResponseEntity` lets you set status codes and headers, for example 201 Created with a Location header.

## Controllers, validation and errors

Spring MVC maps HTTP requests to controller methods, validates input and produces consistent error responses.

![Four ideas: controllers, request mapping, validation, problem details.](assets/figures/spring-boot/section-3-map.svg) — Figure 3.1 — Controllers, mapping, validation and errors.

## The product controller

This file is from a demo "shop" service generated by start.spring.io for Spring Boot 4.1.1 and Java 21 (Temurin 21.0.12), built with Maven 3.9.16; the project compiled and its tests passed. Request and response records, constructor injection, path variables, a query parameter and a 201 response with Location.

```java
package com.example.shop;

import jakarta.validation.Valid;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.net.URI;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

    public record CreateProduct(@NotBlank String name,
                                @NotNull @DecimalMin("0.01") BigDecimal price) { }

    public record ProductView(Long id, String name, BigDecimal price, String currency) { }

    private final ProductService service;
    private final ShopProperties props;

    public ProductController(ProductService service, ShopProperties props) {
        this.service = service;
        this.props = props;
    }

    @GetMapping("/{id}")
    public ProductView get(@PathVariable long id) {
        return view(service.get(id));
    }

    @GetMapping
    public List<ProductView> cheaperThan(@RequestParam BigDecimal max) {
        return service.cheaperThan(max).stream().limit(props.maxPageSize()).map(this::view).toList();
    }

    @PostMapping
    public ResponseEntity<ProductView> create(@Valid @RequestBody CreateProduct body) {
        Product saved = service.create(body.name(), body.price());
        return ResponseEntity.created(URI.create("/api/products/" + saved.getId())).body(view(saved));
    }

    private ProductView view(Product p) {
        return new ProductView(p.getId(), p.getName(), p.getPrice(), props.currency());
    }
}
```

## Calling the API, run

I packaged the demo with mvn package, started it with java -jar on port 8085 and called it with curl; the output is copied from that run. GET returns the product as JSON including the configured currency; POST returns 201 with the new resource's Location.

```bash
curl -s localhost:8085/api/products/1
curl -s -i -X POST -H "content-type: application/json" -d '{"name":"Stapler","price":250}' localhost:8085/api/products
```

Output:

```
{"id":1,"name":"Notebook","price":120.00,"currency":"INR"}
HTTP/1.1 201 
Location: /api/products/4
{"id":4,"name":"Stapler","price":250,"currency":"INR"}
```

**Quiz:** Why return DTO records instead of JPA entities?

- [x] The API stays stable and does not leak database structure
- [ ] Entities cannot be serialised
- [ ] Records are faster to query
- [ ] Spring forbids returning entities

*Answer:* The API stays stable and does not leak database structure. Separate API and persistence models.
