RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Spring Boot questions
MediumSpring Boot#366 min readJul 11, 2026

ResponseEntity.

practice
learning
Practice modeTest yourself instead of reading straight through

Hook: ResponseEntity is the HTTP “receipt” for your API call: it tells the client what happened, what came back, and any extra headers.

Question: What is ResponseEntity in Spring Boot, and why would you use it?

Answer: ResponseEntity is a Spring type that lets you control the full HTTP response: the status code, the response body, and headers. Instead of only returning an object, you can say “return this JSON with 201 Created,” or “return 404 Not Found with no body.” It is especially useful in REST APIs when different outcomes need different HTTP statuses.

Interview-Ready Answer: I use ResponseEntity when I want precise control over an API response. It lets me return the body, HTTP status, and headers together, like 200 OK, 201 Created with a Location header, or 404 Not Found when data is missing. Under the hood, Spring still serializes the body for me, but ResponseEntity gives me the final say on the HTTP response.

🧠 Memory Map
Memory map — visual summary of this topic

What it is

Detailed Explanation: ResponseEntity<T> is a generic wrapper around a response body of type T. Think of it as a small envelope that carries three things: the data, the status code, and headers. The generic part means the body can be a UserDto, a list, a string, or even null.

How it works under the hood

  1. Your controller returns a ResponseEntity instead of a plain object.
  2. Spring reads the status code from the entity and sets it on the HTTP response.
  3. Spring copies any headers from the entity into the HTTP response.
  4. If the body is not empty, Spring finds an HttpMessageConverter (a serializer) to turn it into JSON, XML, or text.
  5. The final HTTP response is written back to the client.

This means ResponseEntity does not itself “serialize JSON”; it only defines what should be sent. Spring MVC does the conversion through message converters such as Jackson for JSON.

When and why to use it

  • Different status codes: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found.
  • Headers matter: for example Location, ETag, Cache-Control, or custom trace IDs.
  • Conditional responses: return a body only when data exists, otherwise return an error status.
  • API clarity: the controller expresses HTTP intent directly, which is easier to test and reason about.

Comparison with alternatives

OptionWhat you controlTypical use
@ResponseBody / @RestControllerBody onlySimple 200 JSON
ResponseEntityStatus, headers, bodyReal REST APIs
@ResponseStatusFixed status on a method or exceptionSimple, static outcomes

Default behavior: if you return an object from a @RestController, Spring usually sends 200 OK. If you need anything else, ResponseEntity is the cleanest tool.

Performance and edge cases

  • Time and space overhead are tiny: creating a ResponseEntity is basically O(1). The real cost is body serialization, not the wrapper.
  • Use 204 No Content when there is no body; sending an empty JSON object can confuse clients.
  • A common gotcha is returning null instead of ResponseEntity; that can trigger confusing errors or framework defaults.
  • If you need custom headers, remember they must be set before the body is written.

Memory Hook: Think “ResponseEntity = response in an envelope.” The body is the letter, the status is the stamp, and the headers are the address labels.

Real-world story

Real-World Example: Imagine a checkout service in an e-commerce app. When an order is placed, the API should return 201 Created, include the new order JSON in the body, and send a Location header pointing to /api/orders/{id}. If the cart is empty, it should return 400 Bad Request. If the order ID is requested later but not found, it should return 404 Not Found.

What goes wrong if a developer ignores ResponseEntity? The controller may always return 200 OK, even for failures. Front-end code then assumes the order was created, retries incorrectly, or shows the wrong success toast. In logs you may see “order not found,” but the browser still treats the call as successful because the status code never changed. That mismatch is a classic production bug: the payload says one thing, the HTTP status says another.

In practice, this matters a lot for mobile apps, gateways, and retries. Clients often make decisions based on status codes first, body second. A clean ResponseEntity makes those decisions reliable.

Spring Boot
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@RestController
class OrderController {

    private final Map<Long, OrderDto> orders = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1000);

    OrderController() {
        // Seed one record so the success and not-found paths are both easy to test.
        orders.put(1000L, new OrderDto(1000L, "book", 1));
    }

    @GetMapping("/api/orders/{id}")
    public ResponseEntity<OrderDto> getOrder(@PathVariable long id) {
        // Optional lets us express “maybe found” clearly, then map it to HTTP semantics.
        return Optional.ofNullable(orders.get(id))
                .map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
    }

    @PostMapping("/api/orders")
    public ResponseEntity<?> createOrder(@RequestBody OrderCreateRequest request) {
        // Manual validation keeps the example runnable without extra validation annotations.
        if (request == null || request.item() == null || request.item().isBlank() || request.quantity() <= 0) {
            return ResponseEntity.badRequest().body(Map.of(
                    "error", "item must be non-empty and quantity must be greater than 0"
            ));
        }

        long id = idGenerator.incrementAndGet();
        OrderDto saved = new OrderDto(id, request.item(), request.quantity());
        orders.put(id, saved);

        // Location tells the client where the new resource lives.
        return ResponseEntity.created(URI.create("/api/orders/" + id))
                .header("X-Order-Source", "demo-store")
                .body(saved);
    }

    @GetMapping("/api/orders")
    public ResponseEntity<List<OrderDto>> listOrders() {
        // A plain 200 response is still explicit when wrapped in ResponseEntity.
        return ResponseEntity.ok(orders.values().stream().toList());
    }
}

record OrderDto(Long id, String item, int quantity) {}
record OrderCreateRequest(String item, int quantity) {}

Follow-up & Tricky Questions:

  • How is ResponseEntity different from @ResponseBody? @ResponseBody tells Spring to write the return value to the HTTP body, but it does not let you set status or headers easily. ResponseEntity gives you body plus full HTTP control.
  • When would you return 201 Created instead of 200 OK? Use 201 when a new resource was created, especially for POST requests. It is best practice to include a Location header that points to the new resource.
  • Can ResponseEntity be empty? Yes. You can return ResponseEntity.noContent().build() for 204 No Content or ResponseEntity.notFound().build() for 404.
  • What happens if the body is already a JSON string? Spring treats it as a plain string unless you set content type appropriately. In most REST APIs, you should return objects and let Spring/Jackson serialize them.
  • How do you add custom headers? Use .header("Name", "value") or build a HttpHeaders object. This is common for trace IDs, caching, or file downloads.
  • Gotcha: Does ResponseEntity.ok(null) mean “no content”? Not exactly. It is still a 200 OK; if you truly have no response body, 204 No Content is the clearer choice.
  • Gotcha: If you return ResponseEntity, do you still need @ResponseBody? No. In a @RestController, the body is already handled for you. ResponseEntity only changes the response details.
  • Gotcha: Is ResponseEntity only for errors? No. It is equally useful for successful responses, especially when you need headers or a status other than 200.

Common Mistakes:

  • Always returning 200. Correction: use ResponseEntity so the HTTP status matches the real outcome.
  • Putting error info only in the body. Correction: set the proper 4xx or 5xx status so clients and proxies can react correctly.
  • Forgetting headers like Location. Correction: include resource location for create flows and cache headers when needed.
  • Using ResponseEntity when a simple return is enough. Correction: for a tiny demo endpoint with always-200 JSON, a plain object can be fine; use ResponseEntity when HTTP details matter.

Memory Hook: “Body, Status, Headers” — if you can remember those three, you know why ResponseEntity exists.

Cheat Sheet:

  • ResponseEntity = body + status + headers.
  • ResponseEntity.ok(body) → 200 OK.
  • ResponseEntity.created(uri).body(body) → 201 Created + Location.
  • ResponseEntity.notFound().build() → 404 with no body.
  • ResponseEntity.noContent().build() → 204 with no body.
  • Use it when HTTP semantics matter, not just data.

Practice Tasks:

  • Add a PUT /api/orders/{id} endpoint that returns 200 when updated and 404 when missing.
  • Modify the create endpoint to return 400 for invalid quantity and 201 for valid input.
  • Add a custom header like X-Request-Id to every successful response and verify it in Postman or curl.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.net.URI; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } @RestController class OrderController { private final Map<Long, OrderDto> orders = new ConcurrentHashMap<>(); private final AtomicLong idGenerator = new AtomicLong(1000); OrderController() { // Seed one record so the success and not-found paths are both easy to test. orders.put(1000L, new OrderDto(1000L, "book", 1)); } @GetMapping("/api/orders/{id}") public ResponseEntity<OrderDto> getOrder(@PathVariable long id) { // Optional lets us express “maybe found” clearly, then map it to HTTP semantics. return Optional.ofNullable(orders.get(id)) .map(ResponseEntity::ok) .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build()); } @PostMapping("/api/orders") public ResponseEntity<?> createOrder(@RequestBody OrderCreateRequest request) { // Manual validation keeps the example runnable without extra validation annotations. if (request == null || request.item() == null || request.item().isBlank() || request.quantity() <= 0) { return ResponseEntity.badRequest().body(Map.of( "error", "item must be non-empty and quantity must be greater than 0" )); } long id = idGenerator.incrementAndGet(); OrderDto saved = new OrderDto(id, request.item(), request.quantity()); orders.put(id, saved); // Location tells the client where the new resource lives. return ResponseEntity.created(URI.create("/api/orders/" + id)) .header("X-Order-Source", "demo-store") .body(saved); } @GetMapping("/api/orders") public ResponseEntity<List<OrderDto>> listOrders() { // A plain 200 response is still explicit when wrapped in ResponseEntity. return ResponseEntity.ok(orders.values().stream().toList()); } } record OrderDto(Long id, String item, int quantity) {} record OrderCreateRequest(String item, int quantity) {}