Spring Boot REST Annotations Deep Dive

1 minute read

This chapter dives into REST annotations, ResponseEntity for full control over responses, and content negotiation.


Mapping Annotations

@RestController
@RequestMapping("/api/books")
public class BookController {

    @GetMapping                        // GET /api/books
    @GetMapping("/{id}")               // GET /api/books/1
    @GetMapping("/search")             // GET /api/books/search?title=Java

    @PostMapping                       // POST /api/books
    @PutMapping("/{id}")               // PUT /api/books/1
    @PatchMapping("/{id}")             // PATCH /api/books/1
    @DeleteMapping("/{id}")            // DELETE /api/books/1
}

These are shortcuts for @RequestMapping(method = RequestMethod.GET).


ResponseEntity — Full Control

ResponseEntity lets you control status code, headers, and body:

@RestController
@RequestMapping("/api/books")
public class BookController {

    @GetMapping("/{id}")
    public ResponseEntity<Book> getById(@PathVariable Long id) {
        return bookService.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<Book> create(@RequestBody Book book) {
        Book created = bookService.create(book);
        URI location = URI.create("/api/books/" + created.getId());

        return ResponseEntity
            .created(location)           // 201 + Location header
            .body(created);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        bookService.delete(id);
        return ResponseEntity.noContent().build();  // 204
    }
}

ResponseEntity Builder Methods

Method Status Code
ResponseEntity.ok(body) 200
ResponseEntity.created(uri).body(obj) 201
ResponseEntity.accepted() 202
ResponseEntity.noContent().build() 204
ResponseEntity.badRequest().body(errors) 400
ResponseEntity.notFound().build() 404
ResponseEntity.status(HttpStatus.CONFLICT).body(msg) 409

Custom Headers

@GetMapping("/{id}")
public ResponseEntity<Book> getById(@PathVariable Long id) {
    Book book = bookService.findById(id);

    return ResponseEntity.ok()
        .header("X-Book-Author", book.getAuthor())
        .header("Cache-Control", "max-age=3600")
        .body(book);
}

Produces and Consumes

@PostMapping(
    consumes = MediaType.APPLICATION_JSON_VALUE,    // accepts JSON
    produces = MediaType.APPLICATION_JSON_VALUE      // returns JSON
)
public ResponseEntity<Book> create(@RequestBody Book book) {
    return ResponseEntity.created(uri).body(bookService.create(book));
}

// Accept XML too
@GetMapping(produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Book> getAll() {
    return bookService.findAll();
}

DTOs (Data Transfer Objects)

Never expose your entity directly — use DTOs:

// Request DTO
public record CreateBookRequest(
    String title,
    String author,
    String isbn,
    BigDecimal price
) {}

// Response DTO
public record BookResponse(
    Long id,
    String title,
    String author,
    String isbn,
    BigDecimal price,
    LocalDate publishedDate
) {
    public static BookResponse from(Book book) {
        return new BookResponse(
            book.getId(),
            book.getTitle(),
            book.getAuthor(),
            book.getIsbn(),
            book.getPrice(),
            book.getPublishedDate()
        );
    }
}
@PostMapping
public ResponseEntity<BookResponse> create(@RequestBody CreateBookRequest request) {
    Book book = bookService.create(request);
    BookResponse response = BookResponse.from(book);
    URI location = URI.create("/api/books/" + book.getId());
    return ResponseEntity.created(location).body(response);
}

Key Takeaways

  1. Use specific mapping annotations: @GetMapping, @PostMapping, etc.
  2. ResponseEntity gives full control — status, headers, body.
  3. Always return proper HTTP status codes (201 for created, 204 for deleted).
  4. Use DTOs — never expose JPA entities directly in API responses.
  5. Set Location header on POST/201 responses.

What’s Next?

Next: Request Parameters — @PathVariable, @RequestParam, @RequestBody, pagination.

Updated: