REST APIs are the most common thing built with Spring Boot.
A class annotated @RestController handles HTTP requests; each method's return value is serialised to the response body, as JSON by default via Jackson. @RequestMapping("/api/books") on the class sets a common path prefix.
Methods are mapped with @GetMapping, @PostMapping, @PutMapping, @PatchMapping and @DeleteMapping. Parameters bind parts of the request:
@PathVariable Long idtakes{id}from the path/api/books/{id}and converts it to the declared type; a failed conversion gives a 400.@RequestParam(defaultValue = "0") int pagereads?page=2. Parameters are required unless they have a default orrequired = false(or areOptional).@RequestHeaderreads headers.
Return a domain object, a List, or a Java record: Jackson turns record components into JSON fields. To control status and headers, return ResponseEntity<T>: ResponseEntity.ok(book) or ResponseEntity.notFound().build().
Controllers should be thin: translate HTTP to method calls on a @Service and back. Business logic in controllers is hard to test and reuse.
Spring MVC runs each request on a thread from Tomcat's pool (or a virtual thread if enabled), so controllers must be stateless, like all singleton beans.
Example
record Book(long id, String title, String author) {}
@RestController
@RequestMapping("/api/books")
class BookController {
private final Map<Long, Book> books = Map.of(
1L, new Book(1, "Dune", "Herbert"),
2L, new Book(2, "Emma", "Austen"));
@GetMapping
List<Book> list(@RequestParam(defaultValue = "") String author) {
return books.values().stream()
.filter(b -> author.isEmpty() || b.author().equalsIgnoreCase(author))
.toList();
}
@GetMapping("/{id}")
ResponseEntity<Book> get(@PathVariable long id) {
Book b = books.get(id);
return b == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(b);
}
}
// GET /api/books?author=austen -> [{"id":2,"title":"Emma","author":"Austen"}]
// GET /api/books/7 -> 404 GET /api/books/abc -> 400Remember
@RestControllermethods return objects serialised to JSON@PathVariablebinds path segments,@RequestParamquery parameters- Return
ResponseEntityto control status codes and headers - Keep controllers thin; delegate to services
Watch out for
- Returning null for a missing resource, which gives 200 with an empty body instead of 404.