Spring Boot

Capstone – Article 5: Global Exception Handling (Expense Tracker)

By Utility Zone · 2026-01-27T18:32:01.447634

1. Introduction

Errors will happen. What matters is how your API responds when they do.

Without global exception handling:

  • Stack traces leak to clients
  • Error responses are inconsistent
  • Controllers become messy

In this article, we’ll build clean, centralized, professional error handling.


2. Problems With Naive Error Handling

Example (❌ bad practice):

if (user == null) {
    throw new RuntimeException("User not found");
}

Problems:

  • Always returns 500
  • No clear error structure
  • Hard for frontend to handle

We need meaningful HTTP status codes + structured errors.


3. Creating Custom Exceptions

3.1 Create Exception Package

com.example.expensetracker.exception

3.2 UserNotFoundException

package com.example.expensetracker.exception;

public class UserNotFoundException extends RuntimeException {

    public UserNotFoundException(String message) {
        super(message);
    }
}

4. Throwing Exceptions From Service Layer

Update UserService:

public User getUserById(Long id) {
    return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException("User not found with id " + id));
}

✔ Business layer throws exceptions
✔ Controller stays clean


5. Creating Global Exception Handler

5.1 @RestControllerAdvice

package com.example.expensetracker.exception;

import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<Map<String, Object>> handleUserNotFound(UserNotFoundException ex) {

        Map<String, Object> error = new HashMap<>();
        error.put("timestamp", LocalDateTime.now());
        error.put("status", HttpStatus.NOT_FOUND.value());
        error.put("error", "Not Found");
        error.put("message", ex.getMessage());

        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
}

6. Handling Validation Errors

Spring throws:

MethodArgumentNotValidException

Handle it globally:

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationErrors(
        MethodArgumentNotValidException ex) {

    Map<String, String> errors = new HashMap<>();

    ex.getBindingResult().getFieldErrors()
        .forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));

    return ResponseEntity.badRequest().body(errors);
}

Produces clear field-level errors.


7. Handling Generic Exceptions

@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleGenericException(Exception ex) {

    Map<String, Object> error = new HashMap<>();
    error.put("timestamp", LocalDateTime.now());
    error.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
    error.put("error", "Internal Server Error");
    error.put("message", "Something went wrong");

    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}

✔ Prevents stack traces from leaking
✔ Safe default response


8. Testing Error Responses

8.1 User Not Found

GET /users/999

Response:

{
  "timestamp": "2026-01-27T10:30:00",
  "status": 404,
  "error": "Not Found",
  "message": "User not found with id 999"
}

8.2 Validation Error

POST /users

Response:

{
  "email": "Email must be valid",
  "password": "Password must be at least 6 characters"
}

9. Why This Matters in Interviews

Interviewers look for: ✔ Proper HTTP status codes
✔ Clean error structure
✔ Centralized handling
✔ No try-catch in controllers

This article alone can level up your backend profile.


10. Common Mistakes

❌ Catching exceptions in controllers
❌ Returning 200 for errors
❌ Exposing stack traces
❌ Inconsistent error formats


11. Git Commit (Important)

Commit your changes:

git add .
git commit -m "Add global exception handling for user APIs"

12. What You Should Have Now

At this point:

  • Clean error responses
  • Centralized exception handling
  • Proper HTTP status codes
  • Enterprise-style API behavior

13. What’s Next?

➡ Capstone – Article 6: Password Encryption & Login Preparation

  • Password hashing
  • BCrypt
  • Preparing for authentication

Type Next when you’re ready 🚀