Skip to content

Spring Boot ResponseEntity and Response Codes | 1xx, 2xx, 3xx, 4xx and 5xx Return Codes

The Formal Business Letter Analogy

Imagine receiving an official letter from your bank. The letter does not consist solely of a raw paragraph of text on a blank scrap of paper. A formal business letter has three distinct structural parts:

  1. The Header & Status Banner: The letterhead indicating the official banking institution, date, reference code, and status: "APPROVED", "PENDING", or "REJECTED".
  2. The Metadata Envelope: Custom transmission details, stamps, security classifications, and tracking numbers.
  3. The Body: The actual contractual statement, account details, or terms.

In HTTP web communications, an HTTP response has that exact three part structure: the status line (e.g. HTTP/1.1 200 OK), the headers (Content-Type, Cache-Control), and the body (the JSON payload).

If a Spring Boot controller method simply returns a plain Java object like return new User(...), you lose control over HTTP status codes and custom headers: Spring will default to 200 OK for everything, even when creating a brand new resource or encountering an error. ResponseEntity represents the entire HTTP response — status code, headers, and body — giving you complete programmatic control over how your API communicates with clients.

This lecture covers the structure of ResponseEntity, fluent builder methods, the five categories of HTTP status return codes, and production patterns for returning clean API responses.


The Anatomy of an HTTP Response

Every HTTP response sent over the wire contains three sections:

+-------------------------------------------------------------+
| Status Line:  HTTP/1.1 201 Created                          |
+-------------------------------------------------------------+
| Headers:      Content-Type: application/json                |
|               Location: /api/v1/users/42                     |
|               X-Execution-Time: 12ms                        |
+-------------------------------------------------------------+
| Body (JSON):                                                |
| {                                                           |
|   "id": 42,                                                 |
|   "name": "Alice",                                          |
|   "status": "ACTIVE"                                        |
| }                                                           |
+-------------------------------------------------------------+

When you return a plain object:

java
@PostMapping("/users")
public User createUser(@RequestBody UserDto dto) {
    return userService.create(dto); // Returns 200 OK by default!
}

HTTP REST standards dictate that creating a resource should return 201 Created, not 200 OK. Furthermore, you cannot attach custom headers (like Location or execution timings) with a plain return type.

ResponseEntity<T> solves this by modeling the entire response package.


What Is ResponseEntity?

ResponseEntity<T> is a generic class provided by Spring Framework in org.springframework.http.ResponseEntity. It extends HttpEntity<T>:

java
public class ResponseEntity<T> extends HttpEntity<T> {
    private final Object status;

    // Constructors and fluent builders
}

Because it is a generic wrapper, ResponseEntity<User> represents an HTTP response whose body contains a User object, while ResponseEntity<Void> represents a response with headers and status code but zero response body (ideal for DELETE operations).


Building Responses: Fluent Builder API

Spring provides a fluent, readable builder API on ResponseEntity:

1. Returning 200 OK

java
// Option A: Quick static helper
return ResponseEntity.ok(user);

// Option B: Fluent builder
return ResponseEntity.ok()
    .header("Custom-Header", "Value")
    .body(user);

2. Returning 201 Created with Location Header

According to REST guidelines, creating a resource should return 201 Created and a Location header pointing to the new resource URI:

java
URI location = ServletUriComponentsBuilder
    .fromCurrentRequest()
    .path("/{id}")
    .buildAndExpand(savedUser.getId())
    .toUri();

return ResponseEntity.created(location).body(savedUser);

3. Returning 204 No Content

When a resource is deleted or an action succeeds without needing a response body:

java
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
    userService.delete(id);
    return ResponseEntity.noContent().build();
}

4. Returning 404 Not Found

java
if (user == null) {
    return ResponseEntity.notFound().build();
}

5. Returning Custom Status Codes and Headers

java
return ResponseEntity.status(HttpStatus.ACCEPTED)
    .header("X-Job-Id", "job-9981")
    .body("Request accepted for asynchronous processing");

The Five Categories of HTTP Status Codes

Understanding status code categories is essential for API design and technical interviews:

1xx (Informational) -> Protocol handshakes, preliminary notification
2xx (Success)       -> Request was received, understood, and accepted
3xx (Redirection)   -> Client must take additional action to complete request
4xx (Client Error)  -> Client sent invalid data, wrong URL, or bad authentication
5xx (Server Error)  -> Server crashed, threw unhandled exception, or timed out

1. 1xx: Informational

Rarely used in standard REST APIs.

  • 100 Continue: The client should continue sending the request body.
  • 101 Switching Protocols: Upgrading connection (e.g. from HTTP to WebSocket).

2. 2xx: Success

The gold standard for successful operations:

  • 200 OK: Standard success response for GET, PUT, or PATCH.
  • 201 Created: Successful creation of a resource via POST.
  • 202 Accepted: Request accepted for background batch processing; not yet completed.
  • 204 No Content: Action succeeded, but no data is returned (common for DELETE).

3. 3xx: Redirection

  • 301 Moved Permanently: Resource has permanently relocated to a new URL.
  • 302 Found / 307 Temporary Redirect: Temporary redirect to an alternate URL.
  • 304 Not Modified: Client's cached copy is still fresh (conditional GET with ETag).

4. 4xx: Client Error

The fault lies with the caller:

  • 400 Bad Request: Malformed JSON, missing required parameters, validation failure.
  • 401 Unauthorized: Missing or invalid authentication token (client is unauthenticated).
  • 403 Forbidden: Authenticated user lacks permission / role for this resource.
  • 404 Not Found: Target URI or specific database record does not exist.
  • 405 Method Not Allowed: Calling a POST endpoint with a GET request.
  • 409 Conflict: Resource collision (e.g. attempting to register with an email that already exists).
  • 429 Too Many Requests: Rate limit ceiling exceeded.

5. 5xx: Server Error

The fault lies with the server (application bug or infrastructure outage):

  • 500 Internal Server Error: Unhandled exception, null pointer exception, database crash.
  • 502 Bad Gateway: Invalid response from an upstream backend or proxy.
  • 503 Service Unavailable: Server is overloaded or down for maintenance.
  • 504 Gateway Timeout: Downstream microservice failed to respond within timeout window.

Standardizing Error Responses

In production systems, avoid returning raw string errors like ResponseEntity.badRequest().body("invalid"). Instead, return a standardized JSON error object adhering to RFC 7807 (Problem Details for HTTP APIs):

java
public class ApiErrorResponse {
    private LocalDateTime timestamp;
    private int status;
    private String error;
    private String message;
    private String path;

    public ApiErrorResponse(HttpStatus status, String message, String path) {
        this.timestamp = LocalDateTime.now();
        this.status = status.value();
        this.error = status.getReasonPhrase();
        this.message = message;
        this.path = path;
    }

    // Getters and setters
}

In your controller or exception handler:

java
@GetMapping("/users/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
    Optional<User> user = userService.findById(id);

    if (user.isEmpty()) {
        ApiErrorResponse error = new ApiErrorResponse(
            HttpStatus.NOT_FOUND,
            "User with id " + id + " does not exist",
            "/api/users/" + id
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }

    return ResponseEntity.ok(user.get());
}

Interview Questions & Pitfalls

Q1: What is the difference between @ResponseStatus and ResponseEntity?

@ResponseStatus is an annotation placed on a method or exception class to declare a static, hardcoded HTTP status code (e.g. @ResponseStatus(HttpStatus.CREATED)). ResponseEntity is a generic programmatic return type that allows you to vary status codes, headers, and body dynamically based on runtime branching logic inside the method.

Q2: What is the difference between 401 Unauthorized and 403 Forbidden?

401 Unauthorized means the client has not provided valid authentication credentials (they are unauthenticated / identity is unknown). 403 Forbidden means the server knows who the client is, but that authenticated user does not have the required permissions or roles to access the resource (they are unauthorized).

Q3: When should an API return 204 No Content instead of 200 OK?

204 No Content should be returned when the server has successfully fulfilled the request, but there is no payload body to return to the client. This is standard for DELETE operations or status updates that do not produce output data, saving network bandwidth by omitting response bodies.

Q4: Why should POST requests creating resources return 201 Created with a Location header?

Returning 201 Created explicitly signals to the client that a new resource was persisted. The Location header supplies the canonical URL where the newly created resource can be retrieved (e.g. Location: /api/orders/101), following REST architectural conventions.

Q5: What is the difference between 400 Bad Request and 422 Unprocessable Entity?

400 Bad Request typically signals syntactic errors: malformed JSON syntax, invalid HTTP headers, or missing query parameters. 422 Unprocessable Entity signals semantic validation errors: the JSON syntax is perfectly valid, but the data fails business validation rules (such as a negative price or an invalid email format).