JAX-RS — How to return JSON and HTTP status code together?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In JAX-RS, returning JSON together with an HTTP status code is normal and built into the Response API. You do not need two separate return channels. The entity becomes the JSON body, and the Response object carries the status, headers, and media type.

The important design choice is whether you want to return a plain domain object for simple 200 OK cases or build an explicit Response when you need control over status codes and metadata.

The Basic Pattern With Response

Use Response.status(...) or Response.ok(...), attach the entity, and build the response.

java
1import jakarta.ws.rs.GET;
2import jakarta.ws.rs.Path;
3import jakarta.ws.rs.PathParam;
4import jakarta.ws.rs.Produces;
5import jakarta.ws.rs.core.MediaType;
6import jakarta.ws.rs.core.Response;
7
8@Path("/users")
9@Produces(MediaType.APPLICATION_JSON)
10public class UserResource {
11
12    @GET
13    @Path("/{id}")
14    public Response getUser(@PathParam("id") long id) {
15        User user = findUser(id);
16        if (user == null) {
17            ErrorPayload error = new ErrorPayload("USER_NOT_FOUND", "No user with that id");
18            return Response.status(Response.Status.NOT_FOUND)
19                    .entity(error)
20                    .build();
21        }
22
23        return Response.ok(user).build();
24    }
25
26    private User findUser(long id) {
27        return id == 1 ? new User(1, "Alice") : null;
28    }
29
30    public record User(long id, String name) {}
31    public record ErrorPayload(String code, String message) {}
32}

If your JAX-RS runtime has a JSON provider configured, both User and ErrorPayload will be serialized as JSON automatically.

When You Can Return the Object Directly

If the endpoint always returns 200 OK, you can often return the object directly instead of wrapping it in Response.

java
1@GET
2@Path("/health")
3@Produces(MediaType.APPLICATION_JSON)
4public HealthResponse health() {
5    return new HealthResponse("ok");
6}
7
8public record HealthResponse(String status) {}

That is fine for simple cases. The reason people switch to Response is not JSON serialization. It is status-code control.

Returning Custom Status Codes

Suppose you create a resource and want to return 201 Created.

java
1import jakarta.ws.rs.POST;
2import jakarta.ws.rs.core.UriBuilder;
3import java.net.URI;
4
5@POST
6public Response createUser(User user) {
7    long id = 42;
8    URI location = UriBuilder.fromPath("/users/{id}").build(id);
9
10    return Response.status(Response.Status.CREATED)
11            .location(location)
12            .entity(new User(id, user.name()))
13            .build();
14}

Now the response carries:

  • status code 201
  • 'Location header'
  • JSON body containing the created resource

That is exactly what the Response builder is for.

Error Responses Should Be Structured Too

Do not return plain text errors if the rest of your API is JSON. Clients should not have to parse two unrelated formats.

A consistent JSON error payload is easier to consume:

java
return Response.status(Response.Status.BAD_REQUEST)
        .entity(new ErrorPayload("INVALID_INPUT", "Email is required"))
        .build();

This gives clients both machine-readable and human-readable information.

Media Type Still Matters

Returning JSON is not just about the entity object. The endpoint should also declare JSON production with @Produces(MediaType.APPLICATION_JSON) or equivalent application-wide configuration.

Without a JSON provider or the correct media type configuration, your code may compile but fail at runtime or negotiate the wrong representation.

Common Pitfalls

A common mistake is thinking you must manually serialize the object to a JSON string. In normal JAX-RS applications, the JSON provider handles that for you.

Another mistake is returning raw strings for errors while returning objects for success. That makes the API inconsistent for clients.

Developers also sometimes return a domain object directly when different status codes are needed. That works for fixed success responses, but Response is the right tool when HTTP semantics matter.

Finally, do not forget the JSON provider. If the runtime cannot serialize your entity type, the Response object will still exist, but the request will fail during entity writing.

Summary

  • In JAX-RS, return JSON and status codes together by building a Response.
  • Use .entity(...) for the JSON payload and .status(...) or .ok(...) for the HTTP status.
  • Return plain objects directly only when the default success response is sufficient.
  • Keep error responses structured as JSON too.
  • Make sure your endpoint declares JSON production and your runtime has a JSON provider configured.

Course illustration
Course illustration

All Rights Reserved.