I was thinking what is the best way to represent response object in REST API. So, I started thinking about all the things response object should provide to work effectively. Below is the list of things I want my response object to have:
- A single object structure to handle both successful and error scenarios
- Ability to handle multiple types of objects
- Ability to easily figure out if request failed or succeeded
- Store the error code and error message
The code in this post uses Java as the language of choice. As I use Spring Boot to build REST APIs so Jackson is the default JSON serialisation library.
The code below shows the ApiResponse
object that captures both the success and error payload.
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
public final class ApiResponse<T> {
private ErrorResponse error;
private T data;
private Status status;
private ApiResponse(Status status, ErrorResponse error) {
this.error = error;
this.status = status;
}
private ApiResponse(Status status, T data) {
this.data = data;
this.status = status;
}
@JsonCreator
public static <T> ApiResponse<T> success(
@JsonProperty("status") Status status,
@JsonProperty("data") T data) {
return new ApiResponse<>(status, data);
}
@JsonCreator
public static <T> ApiResponse<T> error(
@JsonProperty("status") Status status,
@JsonProperty("error") ErrorResponse error) {
return new ApiResponse<>(status, error);
}
public enum Status {
SUCCESS("success"), ERROR("error");
private final String status;
Status(String status) {
this.status = status;
}
@JsonValue
public String getStatus() {
return this.status;
}
}
public ErrorResponse getError() {
return this.error;
}
public T getData() {
return this.data;
}
public Status getStatus() {
return this.status;
}
}
The key points in the class shown above are:
- Status enum will hold the status of the request. User can just look at this request to figure out whether request succeeded or failed.
- The class is generic so it can be used to store any type of object in the
data
.
- User can get more details about the error from the
error
object.
The ErrorResponse
class is shown below.
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public final class ErrorResponse {
public final String code;
public final String message;
@JsonCreator
public ErrorResponse(
@JsonProperty("code") String code,
@JsonProperty("message") String message) {
this.code = code;
this.message = message;
}
}
The ErrorResponse
class has two fields code and message. The code can be HTTP code or any business specific code. The message variable gives details about the error.
I found the above response classes work great for the REST APIs that I recently built.
Example of successful response is shown below.
{
"status": "success",
"data": {
"task": "Write a post",
"taskStatus":"in_progress",
"tags":["writing"]
}
}
Example of failure response is show below.
{
"status": "error",
"error": {
"code" "409",
"message" : "User with username xyz already exists"
}
}
One thing that you should ensure is that your JSON serialisation library exclude null values. For example, in case of error scenario you don’t want data
field to be null. If you are using Spring Boot, you can instruct Jackson to exclude null fields by specifying a property shown below.
spring.jackson.default-property-inclusion=NON_NULL
If you are using Java 8 or Google’s Gauva and want to exclude Optional type as well then you should use NON_ABSENT
value.
spring.jackson.default-property-inclusion=NON_ABSENT