REST Top

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

>> CHECK OUT THE COURSE
Spring Top – Temp

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

>> LEARN SPRING
Lightrun – Third Party Code

We rely on other people’s code in our own work. Every day. It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.

The problem is, of course, when things fall apart in production - debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky. It’s difficult to understand what talks to what and, specifically, which part of the underlying library is at fault.

Lightrun is a new kind of debugger.

It's one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics. No hotfixes, redeployments, or restarts required.

Learn more in this quick, 5-minute Lightrun tutorial:

>> The Essential List of Spring Boot Annotations and Their Use Cases

1. Overview

In this tutorial, we'll discuss the main differences between Swagger’s @ApiOperation and @ApiResponse annotations.

2. Descriptive Documentation With Swagger

When we create a REST API, it's important to create its proper specification as well. Additionally, such a specification should be readable, understandable, and provide all essential information.

Moreover, the documentation should have a description of every change made on the API. It'd be exhausting and, more importantly, time-consuming to create REST API documentation manually. Fortunately, tools like Swagger can help us with this process.

Swagger represents a set of open-source tools built around OpenAPI Specification. It can help us design, build, document, and consume REST APIs.

The Swagger Specification is a standard for documenting REST APIs. Using Swagger Specification we can describe our entire API, such as exposed endpoints, operations, parameters, authentication methods, and so on.

Swagger provides various annotations that can help us document REST API. Moreover, it provides the @ApiOperation and @ApiResponse annotations to document responses for our REST API. In the remainder of this tutorial, we'll use the below controller class and see how to use these annotations:

@RestController
@RequestMapping("/customers")
class CustomerController {

   private final CustomerService customerService;

   public CustomerController(CustomerService customerService) {
       this.customerService = customerService;
   }
  
   @GetMapping("/{id}")
   public ResponseEntity<CustomerResponse> getCustomer(@PathVariable("id") Long id) {
       return ResponseEntity.ok(customerService.getById(id));
   }
}

3. @ApiOperation

The @ApiOperation annotation is used to describe a single operation. An operation is a unique combination of a path and an HTTP method.

Additionally, using @ApiOperation, we can describe the result of a successful REST API call. In other words, we can use this annotation to specify the general return type.

Let's add the annotation to our method:

@ApiOperation(value = "Gets customer by ID", 
        response = CustomerResponse.class, 
        notes = "Customer must exist")
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomer(@PathVariable("id") Long id) {
    return ResponseEntity.ok(customerService.getById(id));
}

Next, we'll go through some of the most used properties within @ApiOperation.

3.1. The value Property

The required value property contains the operation's summary field. Simply put, it provides a short description of the operation. However, we should keep this parameter shorter than 120 characters.

Here's how we define the value property inside the @ApiOperation annotation:

@ApiOperation(value = "Gets customer by ID")

3.2. The notes Property

Using notes, we can provide more details about the operation. For instance, we can place a text describing the endpoint's restrictions:

@ApiOperation(value = "Gets customer by ID", notes = "Customer must exist")

3.3. The response Property

The response property contains the response type of the operation. In addition, setting this property would override any automatically-derived data type. The response property defined inside the @ApiOperation annotation should contain the general response type.

Let’s create the class that will represent the successful response our method returns:

class CustomerResponse {
  
   private Long id;
   private String firstName;
   private String lastName;
  
   // getters and setters
}

Next, let's add the response property to our annotation:

@ApiOperation(value = "Gets customer by ID",
        response = CustomerResponse.class,
        notes = "Customer must exist")
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomer(@PathVariable("id") Long id) {
    return ResponseEntity.ok(customerService.getById(id));
}

3.4. The code Property

The code property represents the HTTP status of the response code. There are several definitions of HTTP Status Codes. One of them should be used. If we don't provide it, the default value will be 200.

4. @ApiResponse

It's a common practice to return errors using HTTP status codes. We can use the @ApiResponse annotation to describe the concrete possible response of an operation.

While the @ApiOperation annotation describes an operation and a general return type, the @ApiResponse annotation describes the rest of the possible return codes.

Furthermore, the annotation can be applied at the method level as well as at the class level. Moreover, annotation put on the class level will be parsed only if an @ApiResponse annotation with the same code is not already defined on the method level. In other words, the method annotations have precedence over class annotations.

We should use the @ApiResponse annotations within the @ApiResponses annotation, whether we have one or multiple responses. If we use this annotation directly, it will not be parsed by Swagger.

Let's define the @ApiResponses and @ApiResponse annotations on our method:

@ApiResponses(value = {
        @ApiResponse(code = 400, message = "Invalid ID supplied"),
        @ApiResponse(code = 404, message = "Customer not found")})
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomer(@PathVariable("id") Long id) {
    return ResponseEntity.ok(customerService.getById(id));
}

We can use the annotation to specify the success response as well:

@ApiOperation(value = "Gets customer by ID", notes = "Customer must exist")
@ApiResponses(value = {
        @ApiResponse(code = 200, message = "OK", response = CustomerResponse.class),
        @ApiResponse(code = 400, message = "Invalid ID supplied"),
        @ApiResponse(code = 404, message = "Customer not found"),
        @ApiResponse(code = 500, message = "Internal server error", response = ErrorResponse.class)})
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomer(@PathVariable("id") Long id) {
    return ResponseEntity.ok(customerService.getById(id));
}

If we specify the successful response using the @ApiResponse annotation, there's no need to define it inside the @ApiOperation annotation.

Now, let's go through some of the properties used within @ApiResponse.

4.1. The code and message Properties

Both code and message properties are required parameters in the @ApiResponse annotation.

As with the code property inside the @ApiOperation annotation, it should contain the HTTP status code of the response. It's important to mention we cannot define more than one @ApiResponse with the same code property.

The message property usually contains a human-readable message that goes along with the response:

@ApiResponse(code = 400, message = "Invalid ID supplied")

4.2. The response Property

Sometimes, an endpoint uses different response types. For example, we can have one type for success response and another for error response. We can describe them using the optional response property by associating a response class with a response code.

Firstly, let's define a class that will be returned in case of an internal server error:

class ErrorResponse {

    private String error;
    private String message;

    // getters and setters
}

Secondly, let's add a new @ApiResponse for internal server errors:

@ApiResponses(value = {
        @ApiResponse(code = 400, message = "Invalid ID supplied"),
        @ApiResponse(code = 404, message = "Customer not found"),
        @ApiResponse(code = 500, message = "Internal server error", response = ErrorResponse.class)})
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomer(@PathVariable("id") Long id) {
    return ResponseEntity.ok(customerService.getById(id));
}

5. Differences Between @ApiOperation and @ApiResponse

To sum up, the following table shows the main differences between the @ApiOperation and @ApiResponse annotations:

@ApiOperation @ApiResponse
Used for describing an operation Used for describing the possible response of an operation
Used for a successful response Used for successful and error responses
Can be defined only on the method level Can be defined on a method or class level
Can be used directly Can be used only within the @ApiResponses annotation
The default code property value is 200 Does not have a default code property value

6. Conclusion

In this article, we learnt the differences between the @ApiOperation and @ApiResponse annotations.

As always, the source code for the examples is available over on GitHub.

Spring bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> THE COURSE
REST bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course :

>> CHECK OUT THE COURSE
REST footer banner
Comments are closed on this article!