Skip to main content

Handling API Errors Gracefully: Problem JSON (RFC 7807) + Spring Boot

What is Problem JSON?

Problem is a standardized format for expressing HTTP API errors, defined in RFC 7807. It allows APIs to return consistent, machine-readable error responses with meaningful human context.

Instead of inventing your own structure every time your API throws an error, Problem JSON gives you a consistent format like:


HTTP Example

This shows how a server responds using standard HTTP headers when an error occurs.

HTTP/1.1 400 
Content-Type: application/problem+json
Transfer-Encoding: chunked
Date: Sat, 12 Jul 2025 08:47:50 GMT
Connection: close

JSON Example

This is the actual body of the error response returned by the API.

{
  "type": "https://example.com/invalid-employee-id",
  "title": "Bad Request",
  "status": 400,
  "detail": "Employee ID must be a positive number.",
  "instance": "/api/employees/-5"
}

Why Use Problem JSON?

When designing APIs, consistency and clarity in error handling are just as important as the successful responses. Problem JSON helps you standardize how errors are communicated between your backend and frontend systems.

Below are the key reasons why adopting Problem JSON can improve your API's robustness and developer experience.

  • Standardized – Follows RFC 7807, ensuring interoperability across services.
  • Machine-readable + human-friendly – Developers and UIs can both understand it clearly.
  • Easily extended with custom fields – Add extra context like balance, invalid-params, etc.
  • Improves API consistency – One format across all your error responses.
  • Works well with frontend frameworks – Frontend tools (like React, Angular) can parse it easily.


Problem JSON Fields

The table below outlines the standard fields defined by RFC 7807 for Problem JSON responses. While the core fields are standardized, you're free to include additional custom fields to provide extra context—like validation errors, user roles, or account balances.

Field Type Description
type URI A URI identifier for the error type. Can point to documentation.
title string Short, human-readable summary of the problem.
status number HTTP status code.
detail string Detailed human-readable message.
instance URI A URI that identifies the specific request instance.
(custom) any You can add custom fields freely (e.g., balance, invalid-params).

Spring Boot Implementation with Problem JSON

We'll build a REST API that uses Problem JSON to return standard and custom Problem JSON responses in a Spring Boot application.


HTTP Status Scenario
200 OK Successfully fetched employee details
400 Bad Request Invalid input format
404 Not Found Employee ID not found
422 Unprocessable Entity Business rule violation (e.g., duplicate email)
500 Internal Server Error Unexpected server-side error

Zalando’s problem-spring-web is a library that allows Spring Boot applications to produce standardized error responses using RFC 7807.

Why use it?
It automatically converts exceptions to application/problem+json format and gives your APIs predictable and extensible error responses.

1. Add Dependencies

<dependency>
  <groupId>org.zalando</groupId>
  <artifactId>problem-spring-web</artifactId>
  <version>0.29.1</version>
</dependency>

<dependency>
  <groupId>org.zalando</groupId>
  <artifactId>jackson-datatype-problem</artifactId>
  <version>0.27.1</version>
</dependency>

2. Create Global Exception Handler

This will automatically handle Spring exceptions like validation errors, not found, access denied, etc., using Problem JSON format.


3. Sample Controller

This example demonstrates how to use problem-spring-web to handle various HTTP responses (200, 400, 404, 422, 500) in a standardized Problem JSON (RFC 7807) format.


4. Testing

This guide shows how to test different responses from the /api/employees endpoint using curl, Postman, JUnit + MockMvc, and Swagger UI.


1. Using curl

# ✅ 200 OK
curl -i http://localhost:8080/api/employees/1
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 12 Jul 2025 08:38:57 GMT

{"id":1,"name":"John Doe"} 

# ❌ 400 Bad Request
curl -i http://localhost:8080/api/employees/-5
HTTP/1.1 400 
Content-Type: application/problem+json
Transfer-Encoding: chunked
Date: Sat, 12 Jul 2025 08:47:50 GMT
Connection: close

{"type":"https://example.com/invalid-employee-id","title":"Bad Request","status":400,"detail":"Employee ID must be a positive number.","instance":"/api/employees/-5"}

# ❌ 404 Not Found
curl -i http://localhost:8080/api/employees/99
HTTP/1.1 404 
Content-Type: application/problem+json
Transfer-Encoding: chunked
Date: Sat, 12 Jul 2025 08:49:00 GMT

{"type":"https://example.com/employee-not-found","title":"Employee Not Found","status":404,"detail":"No employee exists with ID: 99","instance":"/api/employees/99"}

# ❌ 422 Unprocessable Entity
curl -i -X POST -H "Content-Type: application/json" \
  -d '{"email":"existing@example.com"}' \
  http://localhost:8080/api/employees
HTTP/1.1 422 
Content-Type: application/problem+json
Transfer-Encoding: chunked
Date: Sat, 12 Jul 2025 08:49:30 GMT

{"type":"https://example.com/email-already-registered","title":"Email Already Registered","status":422,"detail":"The email 'existing@example.com' is already used by another employee.","instance":"/api/employees"}% 

# ❌ 500 Internal Server Error
curl -i -X POST -H "Content-Type: application/json" \
  -d '{"email":"trigger500@example.com"}' \
  http://localhost:8080/api/employees
HTTP/1.1 500 
Content-Type: application/problem+json
Transfer-Encoding: chunked
Date: Sat, 12 Jul 2025 08:50:01 GMT
Connection: close

{"title":"Internal Server Error","status":500,"detail":"Simulated internal error."}

2. Using JUnit + MockMvc


3. Using Swagger UI

If you've added Swagger/OpenAPI via springdoc-openapi, you can test endpoints visually:

  1. Ensure you have this dependency:
    <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
      <version>2.1.0</version>
    </dependency>
  2. Run your app and open: http://localhost:8080/swagger-ui.html
  3. Try out GET /api/employees/{id} and POST /api/employees
  4. Swagger will show you both success and error responses with full JSON bodies.

Conclusion

The problem-spring-web library is the easiest and most robust way to integrate RFC 7807 Problem JSON error responses in Spring Boot. It keeps your API consistent, descriptive, and frontend-friendly — without repetitive boilerplate code.

Source Code: GitHub

Comments

Popular posts from this blog

Deploying Spring Boot microservices on Kubernetes Cluster

This article guides you through the deployment of two Spring Boot microservices, namely "order-service" and "inventory-service," on Kubernetes using "MiniKube" . We will establish communication between them, with "order-service" making calls to an endpoint in "inventory-service." Additionally, we will configure "order-service" to be accessible from the local machine's browser . 1) Create Spring Boot microservices The Spring Boot microservices, "order-service" and "inventory-service," have been developed and can be found in this GitHub repository. If you are interested in learning more about creating Spring Boot REST microservices, please refer to this or this (Reactive) link. 2) Build Docker Images The Docker images for both "order-service" and "inventory-service" have already been generated and deployed on DockerHub, as shown below. codeburps/order-service cod...

Circuit Breaker Pattern with Resilience4J in a Spring Boot Application

Read Also: Spring Cloud Circuit Breaker + Resilience4j Resilience4j is a lightweight fault tolerance library that draws inspiration from Netflix Hystrix but is specifically crafted for functional programming. The library offers higher-order functions, known as decorators , designed to augment any functional interface, lambda expression, or method reference with features such as Circuit Breaker, Rate Limiter, Retry, or Bulkhead . These functionalities can be seamlessly integrated within a project, class, or even applied to a single method. It's possible to layer multiple decorators on any functional interface, lambda expression, or method reference, allowing for versatile and customizable fault tolerance. While numerous annotation-based implementations exist online, this article focuses solely on the reactive approach using router predicates and router functions . How Circuit Breaker Pattern works? In general, a circuit breaker functions as an automatic electrical s...

How to create a basic Spring 6 project using Maven

Below is a step-by-step guide to creating a basic Spring project using Maven. 1) Create a Maven Project Use the following Maven command to create a new Maven project. mvn archetype:generate -DgroupId=com.tb -DartifactId=spring-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 2) Import in IntelliJ IDEA If you haven't already, open IntelliJ IDEA on your system. Go to "File" > "New" > "Project from Existing Sources..." . In the file dialog, navigate to the directory where your Maven project is located. Select the pom.xml file within the project directory and click "Open." 3) Update pom.xml In total, the application requires the below-mentioned dependencies: 4) Create Spring Configuration Create a Java configuration class that uses annotations to define your Spring beans and their dependencies. This class should be annotated with @Configuration . 5) Create the Main Application C...