Exception Handling Best Practices in Spring REST API
Fast recall
Quick revision
Core answer
Understanding of production-ready error handling architecture and awareness of security, consistency, and debugging concerns in REST APIs
Catching all exceptions with a single Exception handler returning 500, or worse, sending stack traces to clients in production
Mention the five pillars — global advice, meaningful exceptions, consistent DTOs, correct status codes, server-side logging — and explain why each matters for client experience and debugging
Interview-ready guide
Interview answer
A duplicate business key can be a 409 conflict, while syntactically invalid JSON is a 400 request error. A missing optional search result may validly produce an empty collection rather than 404, so the endpoint contract decides. @RestControllerAdvice keeps translations consistent, but exceptions should retain useful causes for server diagnosis. The client receives a safe code, message, and perhaps request identifier; operators receive structured context through logging and tracing.
Exception Handling Best Practices in Spring REST API
Direct Answer
Use centralized exception handling to translate known failures into a stable HTTP error contract. Map each error from its real meaning rather than applying rigid rules: malformed input is often 400, missing resources 404, state conflicts 409, and unexpected failures 500. Do not expose stack traces or internal SQL. Logging level and detail should depend on whether the event is expected and whether it has already been logged.
Code Example
// Custom exception with context
public class ResourceNotFoundException extends RuntimeException {
private final String resourceType;
private final String resourceId;
public ResourceNotFoundException(String resourceType, String resourceId) {
super(String.format("%s not found with id: %s", resourceType, resourceId));
this.resourceType = resourceType;
this.resourceId = resourceId;
}
}
// Consistent error DTO
record ErrorResponse(String message, int status, String path, LocalDateTime timestamp, List<String> details) {}
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(
ResourceNotFoundException ex, WebRequest request) {
log.warn("Resource not found: {}", ex.getMessage()); // Log for debugging
var error = new ErrorResponse(
ex.getMessage(),
HttpStatus.NOT_FOUND.value(),
request.getDescription(false).replace("uri=", ""),
LocalDateTime.now(),
List.of()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(
Exception ex, WebRequest request) {
log.error("Unexpected error", ex); // Full stack trace in logs
var error = new ErrorResponse(
"An error occurred processing your request", // Safe message
HttpStatus.INTERNAL_SERVER_ERROR.value(),
request.getDescription(false).replace("uri=", ""),
LocalDateTime.now(),
List.of()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
Common Mistakes & Pitfalls
- Catching Exception.class only: Freshers often write a single ExceptionHandler for
Exception.classthat returns 500 for everything. This lumps not-found errors, validation failures, and server bugs into one bucket — clients can't differentiate between "you sent bad data" (400) and "our server crashed" (500). The fix is to handle specific exceptions with correct status codes first, then have a catch-all Exception handler as a last resort. - Returning stack traces to clients: Another common mistake is including
ex.printStackTrace()output or exception class names in the JSON response. This leaks internal implementation details (package names, database types, library versions) that attackers can exploit. Always log the full trace server-side and return a generic message to clients. - Inconsistent error formats: Returning
{"error":"message"}from one endpoint,{"message":"...","code":123}from another, and plain strings from a third. Clients can't reliably parse errors. Define one ErrorResponse record and use it everywhere. - Not logging exceptions: Freshers sometimes return an error response but forget to log the exception. When a production bug happens, there's no trace in the logs to debug.
Check your understanding
Answer these without looking back at the article.
- Why should you never return a stack trace to the API client?
- How do you test exception handling in a Spring Boot REST API?
- What is the difference between application exceptions and system exceptions?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.