DTOs and request validation
Separate wire models from entities and reject bad input before it reaches your logic.
Open this lesson in the learning hubKey points
- Never expose a JPA entity as your API. A DTO lets the database and the contract change independently.
- Java records are ideal DTOs: immutable, tiny, and Jackson binds them via the constructor.
- Annotate the request record with Bean Validation constraints, then add
@Validat the parameter. - A failed check throws
MethodArgumentNotValidExceptionand Boot answers 400, before your code runs. - Validate nested objects too — put
@Validon the nested field, or validation stops at the surface. - Requests and responses are different shapes. A create request has no id; a response does.
Example
import java.util.List;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
public record CreateOrderRequest(
@NotBlank @Email String customerEmail,
@NotEmpty @Valid List<LineItem> items, // @Valid = check each item
@Positive BigDecimal total) {
public record LineItem(
@NotBlank String sku,
@Min(1) @Max(99) int quantity) {}
}
public record OrderResponse(long id, String status, BigDecimal total) {}
@PostMapping
public ResponseEntity<OrderResponse> create(@Valid @RequestBody CreateOrderRequest req) {
// If we get here, req is already valid.
return ResponseEntity.ok(service.create(req));
}
DTOs protect your contract; @Valid stops bad data at the door.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Boot course, and every lesson in it is listed on the Spring Boot contents page.