Stage 11: Spring Boot, lesson 4 of 8

REST APIs, validation and error handling

Intermediate3 min read@since 17Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

Good REST APIs use nouns and HTTP verbs: GET /courses, POST /courses, PUT /courses/{id}, PATCH and DELETE, with the right status codes (201 Created, 400, 404, 409 Conflict, 422).

Validation with Jakarta Bean Validation (spring-boot-starter-validation): annotate DTO fields (@NotBlank, @Email, @Size, @Positive) and add @Valid to the controller parameter.

Global error handling with @RestControllerAdvice maps exceptions to responses. Spring 6+ supports Problem Details (RFC 9457) through ProblemDetail, a standard JSON error format.

Calling other APIs: RestClient (Spring 6.1) for blocking calls, declarative HTTP interface clients, or WebClient for reactive code.

Example

Java
public record CreateCourse(
        @NotBlank @Size(max = 150) String title,
        @NotNull Level level,
        @PositiveOrZero BigDecimal price) {}

@RestControllerAdvice
public class ApiErrors {

    @ExceptionHandler(NotFoundException.class)
    ProblemDetail notFound(NotFoundException ex) {
        ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
        pd.setTitle("Resource not found");
        return pd;
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    ProblemDetail invalid(MethodArgumentNotValidException ex) {
        ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
        pd.setTitle("Validation failed");
        pd.setProperty("errors", ex.getBindingResult().getFieldErrors().stream()
                .map(e -> e.getField() + ": " + e.getDefaultMessage())
                .toList());
        return pd;
    }
}

Common mistake

Forgetting @Valid on the @RequestBody parameter. The annotations on the DTO are then silently ignored.

Under the hood

Make PUT and DELETE idempotent, and accept an Idempotency-Key header on payment POSTs so retries never charge twice. Version your API from day one (a /v1 path or a header; Spring 7 has built-in API versioning). Add springdoc-openapi to get OpenAPI docs and Swagger UI for free.

Check yourself

Which status code means a resource was created?

How this connects

Part of Job-ready backend developer, Microservices and production.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.