Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 23

Crafting Responses with ResponseEntity

Recommended resources

Introduction

Hello! In our previous lesson, we built a crucial safety net for our API by implementing server-side validation. We learned how to use Jakarta Bean Validation annotations like @NotNull and @Size on our DTOs and how to trigger validation with @Valid. We also saw that when validation fails, Spring throws a MethodArgumentNotValidException and returns a default 400 Bad Request response. We then intercepted this exception with a @RestControllerAdvice to create a cleaner error message.

This process highlights a key aspect of API development: controlling the HTTP response is just as important as processing the request. While our exception handler set the status to 400, what if we need to return different status codes based on business logic, or add custom HTTP headers?

That brings us to today's learning outcome: Return appropriate HTTP status codes and headers using ResponseEntity.

In this lesson, you'll gain full command over the HTTP response. We will explore Spring's ResponseEntity class, a powerful tool that allows you to programmatically define the status code, headers, and body of your API's response. This will enable you to build professional and compliant REST APIs that communicate clearly and effectively with clients under any circumstance.


What is ResponseEntity?

At its core, ResponseEntity is a Java class provided by Spring that represents an entire HTTP response. You can think of it as a container for all the pieces you might want to send back to a client.

As you know from your experience with system design, a typical HTTP response consists of three main parts:

  1. Status Code: A number indicating the result of the request (e.g., 200 OK, 201 Created, 404 Not Found).
  2. Headers: Key-value pairs providing metadata about the response (e.g., Content-Type, Cache-Control).
  3. Body: The actual data or payload, typically formatted as JSON in a REST API.

ResponseEntity encapsulates these three components into a single object that you can return from your controller methods.

Spring Boot ResponseEntity Structure
This diagram shows the three core components of a `ResponseEntity`: the HTTP status code, the HTTP headers, and the response body, represented by the generic type `T`.

By default, when you return a plain object (like a DTO) from a @RestController method, Spring automatically wraps it, places it in the response body, and sends a 200 OK status. ResponseEntity is what you use when this default behavior isn't enough.

Using Spring ResponseEntity to Manipulate the HTTP Response

To start, let's get a formal definition of ResponseEntity. This article from Baeldung provides a concise overview.

Read the introduction and the section titled '2. ResponseEntity'. This will introduce the class, its purpose as a generic container, and how Spring handles it when returned from an endpoint.


When to Use ResponseEntity

A common question for developers new to Spring is: "If Spring can automatically create a response, why and when should I use ResponseEntity?" Using it unnecessarily can add boilerplate to your code. The key is to use it when you need its power.

The general principle is:

  • Don't use it if you are simply returning a resource with a 200 OK status. Returning the DTO directly is cleaner.
  • Do use it when you need to programmatically control the response, for example:
    • Returning different status codes based on business logic (e.g., 200 OK if a resource is found, 404 Not Found if it isn't).
    • Adding custom HTTP headers to the response.

This video provides a great, practical discussion on this exact topic.

Spring ResponseEntity - How to customize the response in Spring Boot

Dan Vega's video addresses the practical concern of when to use ResponseEntity versus just returning a DTO directly. It's framed around a common developer debate.

Watch the following parts: Introduction (00:00 - 01:49): The author frames the discussion, making it very relatable for developers. Default Behavior (01:49 - 04:39): See how returning a List<Activity> directly results in a 200 OK response with a JSON body. This is our baseline. Discussion on Consistency (09:26 - 10:48): The author shares a pragmatic viewpoint on the 'always use ResponseEntity for consistency' argument. The key takeaway is: use it when you need it.


Constructing a ResponseEntity

Now for the practical part: how do you create and return a ResponseEntity? Spring offers two main approaches: using constructors and using a fluent builder pattern. The builder pattern is generally preferred in modern Java for its readability.

Setting the Status Code Conditionally

One of the primary uses of ResponseEntity is to return a status that reflects the outcome of an operation.

For example, imagine a GET /users/{id} endpoint. If the user exists, we return 200 OK with the user data. If not, we should return 404 Not Found. ResponseEntity makes this trivial.

Spring provides a convenient HttpStatus enum that contains all standard HTTP status codes, making your code more readable than using raw numbers. ResponseEntity also has a fluent builder API, which is accessed via static methods like ResponseEntity.ok() or ResponseEntity.status().

Using Spring ResponseEntity to Manipulate the HTTP Response

The Baeldung article demonstrates how to construct ResponseEntity instances using its builder pattern. This is the most common and readable approach.

Review the code snippets in the sections '2. ResponseEntity' and the builder examples that follow. Pay close attention to: The conditional logic in the age endpoint example, which returns either HttpStatus.OK or HttpStatus.BAD_REQUEST. The static helper methods like ResponseEntity.ok() and ResponseEntity.badRequest(). The generic builder chain: ResponseEntity.status(HttpStatus.OK).body(...).

Adding Custom Headers

Another key use case for ResponseEntity is adding custom headers. For this, Spring provides the HttpHeaders class, which is essentially a map designed for HTTP headers. You can either construct this object manually or, more fluently, chain .header() calls onto the ResponseEntity builder.

Spring ResponseEntity - How to customize the response in Spring Boot

Let's return to Dan Vega's video to see a practical demonstration of adding a custom header.

Watch from 04:39 to 08:16. The video first shows that ResponseEntity.ok() is equivalent to returning the object directly. Then, it demonstrates how to create an HttpHeaders object, add a custom header, and use a ResponseEntity constructor to include it. This clearly illustrates both the 'why' and 'how' of adding headers.

Using the builder, the code from the video could be written more fluently like this:

@GetMapping
public ResponseEntity<List<Activity>> findAll() {
    return ResponseEntity.ok()
            .header("My-Custom-Header", "my-custom-value")
            .body(activityRepository.findAll());
}

A Crucial Use Case: 201 Created with a Location Header

A fundamental principle of REST is that when a POST request successfully creates a new resource, the server should respond with a 201 Created status code. The response must also include a Location header containing the URL where the new resource can be retrieved.

ResponseEntity provides a dedicated helper method, ResponseEntity.created(URI location), which handles this perfectly. It sets the status code to 201 and adds the Location header in one step.

Spring ResponseEntity - CodingNomads

Correctly handling resource creation is a mark of a well-designed API. This article from CodingNomads provides an excellent guide on how to return a 201 CREATED status with the required Location header.

Read the section 'Using ResponseEntity' and focus on the final examples for a POST request. Notice the progression: Returning 201 CREATED without a Location header (which is not compliant). Manually creating HttpHeaders and adding the Location header. Using the ResponseEntity.created(new URI(...)) builder method. This is the most idiomatic and recommended approach in Spring.

Here is a complete example of this pattern:

import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

@PostMapping("/tasks")
public ResponseEntity<Task> createTask(@RequestBody Task newTask) {
    Task createdTask = taskService.create(newTask);

    // Build the URI for the 'Location' header
    URI location = ServletUriComponentsBuilder.fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(createdTask.getId())
            .toUri();

    // Use the .created() builder to set status to 201 and add the Location header
    return ResponseEntity.created(location).body(createdTask);
}

Alternatives for Simpler Cases

ResponseEntity is powerful, but for simpler cases, it can be overkill. Spring provides the @ResponseStatus annotation as a declarative alternative.

You've already seen @ResponseStatus used on an exception handler method in our previous lesson. You can also apply it directly to a controller method if the success status is always the same and is not 200 OK. For example, if a POST method always results in a 201 Created and you don't need to return the created object's URI in a Location header (though this is not best practice).

Spring ResponseEntity - How to customize the response in Spring Boot

For cases where you only need to change the static success status code, ResponseEntity can be verbose. Let's look at the @ResponseStatus annotation as a simpler alternative.

Watch from 08:16 to 09:39. This short clip shows how to use @ResponseStatus(HttpStatus.CREATED) on a create method to change the default 200 OK to 201 CREATED without wrapping the return type in ResponseEntity.

Here is a summary to guide your choice:

Use Case Recommended Approach Why
Return data with default 200 OK return myDto; Cleanest code, no boilerplate.
Return a fixed, non-200 status (e.g., 201) without a Location header @ResponseStatus(HttpStatus.CREATED) Declarative and simple.
Return status based on runtime logic return ResponseEntity.status(...).body(...) Full programmatic control.
Add custom headers to the response return ResponseEntity.ok().header(...).body(...) The only idiomatic way to add headers dynamically.
POST that creates a new resource return ResponseEntity.created(uri).body(...) Adheres to REST best practices; sets status and Location header.

Conclusion

In this lesson, you've gained a fundamental skill for building professional REST APIs with Spring. By mastering ResponseEntity, you can now craft precise, informative, and standard-compliant HTTP responses for any situation.

Key Takeaways:

  • ResponseEntity gives you complete control over the HTTP response's status, headers, and body.
  • Use it when you need to programmatically determine the status code or add custom headers. For simple 200 OK responses, returning the object directly is cleaner.
  • The builder pattern (ResponseEntity.ok(), .status(), .header()) provides a modern, fluent, and readable way to construct responses.
  • For successful POST requests that create a resource, always return a 201 Created status with a Location header using the ResponseEntity.created(uri) builder.
  • For statically setting a non-200 success status, the @ResponseStatus annotation offers a simpler, declarative alternative.

With this, we conclude our module on building REST APIs with Spring Web MVC. You can now create controllers, handle different types of requests, validate input, and craft detailed responses.

In our next lesson, we will begin a new module, "Application Layering and Data Transfer". We'll shift our focus from the API boundary to the application's internal structure by learning how to organize application logic into a service layer using the @Service annotation. This is a foundational pattern for building maintainable and scalable Spring applications.