REST API with Spring Boot 3 — Part 3
API documentation using Swagger
This is the third post of my REST API series, where I write about my impressions and experience with this architecture style using Java with Spring Boot.
The full series of articles are listed below:
- Part I: Project setup and API implementation with Spring Boot
- Part II: HATEOAS, Root Entry Point and Pagination
- Part III: API documentation using Swagger
- Part IV: API Authentication and Authorization with Spring Security
The implementation of each article is available on GitHub in a specific branch, and the final implementation is on the main branch.
In this article we will add Swagger integration to our existing API to document our endpoints.
The code of this article is available on GitHub in branch ‘ch-03’.
Swagger
Swagger is an interface description language for describing REST APIs, and has indeed become the standard for defining RESTful APIs.
According to swagger.io:
The ability of APIs to describe their own structure is the root of all awesomeness in Swagger. By reading your API’s structure, we can automatically build beautiful and interactive API documentation. We can also automatically generate client libraries for your API in many languages and explore other possibilities like automated testing. Swagger does this by asking your API to return a YAML or JSON that contains a detailed description of your entire API. This file is essentially a resource listing of your API which adheres to OpenAPI Specification.
In my opinion, a good Swagger UI page is the best documentation to get into an API specs, and it is very important for DX (Developer Experience).
Implementation
From our API built in the second article of this series, we’ll add swagger integration to document our API endpoints and generate a json/yml file and a html interactive page.
First of all, let’s add two more dependencies in the pom.xml (SpringDoc):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.1.0</version>
</dependency>
This library will allow the Controller methods to be annotated with Swagger annotations to automatically generate the describing files. It will also enable an UI page (swagger-ui) to view the generated documentation.
We can set the default URL to display the documentation in the application.yml file:
springdoc:
api-docs:
path: /api-docs
And now we can describe our Controller methods (endpoints) with proper annotations:
@RestController
@RequestMapping("/books")
public class BooksController {
@Operation( summary = "Find books", description = "Get registered books" )
@GetMapping
public ResponseEntity<List<BookResponse>> getBooks(
@RequestParam(value = "size", required = false, defaultValue = "3") Integer size,
@RequestParam(value = "page", required = false, defaultValue = "0") Integer pageNumber,
@RequestParam(value = "sort", required = false, defaultValue = "title") String sortField,
@RequestParam(value = "direction", required = false, defaultValue = "ASC") String sortDirection) {
// implementation omitted
}
@Operation(summary = "Find book by id", description = "Find book by id",
responses = { @ApiResponse( responseCode = "404", description = "Book not found",
content = { @Content(schema = @Schema(implementation = ErrorResponse.class)) }) } )
@GetMapping("/{id}")
public ResponseEntity<BookResponse> getBook(@PathVariable("id") Integer id) {
// implementation omitted
}
@Operation(summary = "Register new book", description = "Register new book",
responses = { @ApiResponse( responseCode = "400", description = "Invalid Request data",
content = { @Content(schema = @Schema(implementation = ErrorResponse.class)) }) } )
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // This annotation helps Swagger to automatically generate documentation
public ResponseEntity<BookResponse> createBook(@RequestBody @Valid BookRequest request) {
// implementation omitted
}
@Operation(summary = "Update book info", description = "Update book info",
responses = {
@ApiResponse( responseCode = "400", description = "Invalid Request data",
content = { @Content(schema = @Schema(implementation = ErrorResponse.class)) } ),
@ApiResponse( responseCode = "404", description = "Book not found",
content = { @Content(schema = @Schema(implementation = ErrorResponse.class)) } )
})
@PutMapping("{id}")
public ResponseEntity<BookResponse> updateBook(@PathVariable("id") Integer id, @RequestBody @Valid BookRequest request) {
// implementation omitted
}
@Operation(
summary = "Delete book by id", description = "Delete book by id",
responses = { @ApiResponse( responseCode = "404", description = "Book not found",
content = { @Content(schema = @Schema(implementation = ErrorResponse.class)) }) })
@DeleteMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT) // This annotation helps Swagger to automatically generate documentation
public ResponseEntity deleteBook(@PathVariable("id") Integer id) {
// implementation omitted
}
@Operation( summary = "Find reviews", description = "Get review of a book" )
@GetMapping("{bookId}/reviews")
public ResponseEntity<List<ReviewResponse>> getReviews(@PathVariable("bookId") Integer bookId) {
// implementation omitted
}
@Operation(
summary = "Add new review", description = "Add new review to a book",
responses = {
@ApiResponse( responseCode = "400", description = "Invalid Request data",
content = { @Content(schema = @Schema(implementation = ErrorResponse.class)) } ),
@ApiResponse( responseCode = "404", description = "Book not found",
content = { @Content(schema = @Schema(implementation = ErrorResponse.class)) } )
})
@PostMapping("{bookId}/reviews")
@ResponseStatus(HttpStatus.CREATED) // This annotation helps Swagger to automatically generate documentation
public ResponseEntity<ReviewResponse> createReview(@PathVariable("bookId") Integer bookId, @RequestBody @Valid ReviewRequest request) {
// implementation omitted
}
}
And that’s it! We can run the application and by accessing the “http://localhost:9090/api/v1/api-docs”, we can see the generated json file.
To access the Swagger UI page the URL is “http://localhost:9090/api/v1/swagger-ui/index.html”, where we can see all the operations available and its documentation.
It’s even possible to test the operation by expanding its box and using the “Try it out” button: