Lesson 35 / 38

API Status Codes & Error Handling

Return the right HTTP status for every outcome and centralise error responses with a global @ExceptionHandler instead of scattering try/catch across controllers.

Status codes are part of the contract

200 OK, 201 Created, 204 No Content for success; 400 Bad Request (malformed input), 401 Unauthorized (no/bad credentials), 403 Forbidden (authenticated but not allowed), 404 Not Found, 409 Conflict (duplicate/version clash) for client errors; 500 Internal Server Error for unexpected server failures.

A global exception handler

@RestControllerAdvice centralises exception-to-response mapping across every controller — no controller needs a try/catch for expected failures like a missing resource.

@RestControllerAdvice
class ApiExceptionHandler {
    @ExceptionHandler(OrderNotFoundException.class)
    ResponseEntity<ErrorBody> notFound(OrderNotFoundException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body(new ErrorBody("ORDER_NOT_FOUND", e.getMessage()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    ResponseEntity<ErrorBody> badInput(MethodArgumentNotValidException e) {
        return ResponseEntity.badRequest()
            .body(new ErrorBody("VALIDATION_ERROR", e.getMessage()));
    }
}

Status code check

Quick check: A client sends valid credentials but tries to delete another user's order. What status code fits best?

  • 401 Unauthorized
  • 404 Not Found
  • 403 Forbidden
  • 500 Internal Server Error
Answer

403 Forbidden — 401 means missing/invalid credentials; here the client is authenticated but not allowed to act on this resource — that's 403.