ResponseEntity<T> is how you control the whole HTTP response — status code, headers, and body — instead of letting Spring assume 200 OK with just the body. Returning a bare object (OrderDto) is fine when 200 and default headers are correct; ResponseEntity is what you reach for the moment they aren't.
What it wraps
It's a value object holding three things: an HttpStatus, an HttpHeaders, and an optional body of type T. Spring's return-value handler unpacks all three onto the actual response, running the body through a message converter just as it would a plain return value.
@GetMapping("/{id}")
ResponseEntity<OrderDto> get(@PathVariable Long id) {
return service.findOptional(id)
.map(ResponseEntity::ok) // 200 + body
.orElseGet(() -> ResponseEntity.notFound().build()); // 404, no body
}
When it earns its place
Non-200 success codes. A POST that creates a resource should return 201 Created with a Location header:
@PostMapping
ResponseEntity<OrderDto> create(@Valid @RequestBody CreateOrder cmd) {
OrderDto dto = service.create(cmd);
return ResponseEntity
.created(URI.create("/api/orders/" + dto.id())) // 201 + Location
.body(dto);
}
Custom headers — caching (ETag, Cache-Control), pagination (X-Total-Count), or Content-Disposition for downloads.
Conditional status — 204 No Content for a delete, 404 when missing, 409 Conflict on a clash — chosen at runtime from the same method.
Empty body with meaning — ResponseEntity.noContent().build().
The fluent builder
ResponseEntity exposes a builder: ok(), created(uri), accepted(), noContent(), badRequest(), notFound(), status(HttpStatus), each followed by .header(...) and .body(...) (or .build() for no body).