Spring REST Docs Versus SpringFox Swagger for API Documentation
Want to know more about Spring REST Docs and SpringFox Swagger? Check out this blogger's opinion on when it's best to use these for API documentation.
Join the DZone community and get the full member experience.
Join For FreeRecently, I have come across some articles and mentions about Spring REST Docs, where it has been present as a better alternative to traditional Swagger docs. Until now, I was always using Swagger for building API documentation, so I decided to try Spring REST Docs. You may even read on the main page of that Spring project (https://spring.io/projects/spring-restdocs) some references to Swagger, for example: “This approach frees you from the limitations of the documentation produced by tools like Swagger." Are you interested in building API documentation using Spring REST Docs? Let’s take a closer look at that project!
The first difference, in comparison to Swagger, is a test-driven approach to generating API documentation. Thanks to that, Spring REST Docs ensure that the documentation is always generated accurately and matches the actual behavior of the API. When using Swagger SpringFox library, you just need to enable it for the project and provide some configuration to force it to work following your expectations. I have already described the usage of Swagger 2 for automated build API documentation for Spring Boot-based applications in my two previous articles:
- Microservices API Documentation with Swagger2 – it provides an example of building API documentation for many microservices hidden behind a single API gateway based on Spring Cloud Netflix Zuul
- Versioning REST API with Spring Boot and Swagger – it provides an example of building documentation for different versions of API exposed by a single application
The articles mentioned above describe, in greater detail, how to use SpringFox Swagger in your Spring Boot application to automatically generate API documentation based on the source code. Here, I’ll give you a short introduction to that technology and how to easily find the differences between usage of Swagger2 and Spring REST Docs.
1. Using Swagger2 With Spring Boot
To enable the SpringFox library for your application, you need to include the following dependencies to pom.xml
.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
Then, you should annotate the main or configuration class with @EnableSwagger2
. You can also customize the behavior of SpringFox library by declaring the Docket
bean.
@Bean
public Docket swaggerEmployeeApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("pl.piomin.services.employee.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(new ApiInfoBuilder().version("1.0").title("Employee API").description("Documentation Employee API v1.0").build());
}
Now, after running the application, the documentation is available under the context path /v2/api-docs
. You can also display it in your web browser using the Swagger UI available at site /swagger-ui.html
.
It looks easy? Let’s see how to do this with Spring REST Docs.
2. Using Asciidoctor With Spring Boot
There are some other differences between Spring REST Docs and SpringFox Swagger. By default, Spring REST Docs uses Asciidoctor. Asciidoctor processes plain text and produces HTML, styled and laid out to suit your needs. If you prefer, Spring REST Docs can also be configured to use Markdown. This really distinguished it from Swagger, which uses its own notation called OpenAPI Specification.
Spring REST Docs makes use of snippets that are produced by tests written with Spring MVC’s test framework, Spring WebFlux’s WebTestClient or REST Assured 3. I’ll show you an example based on Spring MVC.
I suggest you begin by creating your base Asciidoc file. It should be placed in the src/main/asciidoc
directory in your application source code. I don’t know if you are familiar with the Asciidoctor notation, but it is really intuitive. The sample shown below demonstrates two important things. First, we’ll display the version of the project taken from pom.xml
. Then, we’ll include the snippets generated during JUnit tests by declaring macro called operation containing document name and list of snippets. We can choose between such snippets like curl-request
, http-request
, http-response
, httpie-request
, links
, request-body
, request-fields
, response-body
, response-fields
or path-parameters
. The document name is determined by name of the test method in our JUnit test class.
= RESTful Employee API Specification
{project-version}
:doctype: book
== Add a new person
A `POST` request is used to add a new person
operation::add-person[snippets='http-request,request-fields,http-response']
== Find a person by id
A `GET` request is used to find a new person by id
operation::find-person-by-id[snippets='http-request,path-parameters,http-response,response-fields']
The source code fragment with Asciidoc notation is just a template. We would like to generate an HTML file, which prettily displays all our automatically generated staff. To achieve it we should enable ,the plugin asciidoctor-maven-plugin
in the project’s pom.xml
. In order to display the Maven project version, we need to pass it to the Asciidoc plugin configuration attributes. We also need to spring-restdocs-asciidoctor
dependency to that plugin.
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.6</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<attributes>
<project-version>${project.version}</project-version>
</attributes>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
</dependencies>
</plugin>
Now, the documentation is automatically generated during Maven to build from our api.adoc
file located inside src/main/asciidoc
directory. But, we still need to develop the JUnit API tests that automatically generate required snippets. Let’s do that in the next step.
3. Generating Snippets for Spring MVC
First, we should enable Spring REST Docs for our project. To achieve this, we have to include the following dependency.
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
Now, all we need to do is to implement the JUnit tests. Spring Boot provides an @AutoConfigureRestDocs
annotation that allows you to leverage Spring REST Docs in your tests.
In fact, we need to prepare the standard Spring MVC test using MockMvc
bean. I also mocked some methods implemented by EmployeeRepository
. Then, I used some static methods provided by Spring REST Docs with support for generating documentation of request and response payloads. First of those methods is the document("{method-name}/",...)
, which is responsible for generating snippets under the directory target/generated-snippets/{method-name}
, where the method name is the name of the test method formatted using kebab-case. I have described all the JSON fields in the requests using requestFields(...)
and responseFields(...)
methods.
@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeController.class)
@AutoConfigureRestDocs
public class EmployeeControllerTest {
@MockBean
EmployeeRepository repository;
@Autowired
MockMvc mockMvc;
private ObjectMapper mapper = new ObjectMapper();
@Before
public void setUp() {
Employee e = new Employee(1L, 1L, "John Smith", 33, "Developer");
e.setId(1L);
when(repository.add(Mockito.any(Employee.class))).thenReturn(e);
when(repository.findById(1L)).thenReturn(e);
}
@Test
public void addPerson() throws JsonProcessingException, Exception {
Employee employee = new Employee(1L, 1L, "John Smith", 33, "Developer");
mockMvc.perform(post("/").contentType(MediaType.APPLICATION_JSON).content(mapper.writeValueAsString(employee)))
.andExpect(status().isOk())
.andDo(document("{method-name}/", requestFields(
fieldWithPath("id").description("Employee id").ignored(),
fieldWithPath("organizationId").description("Employee's organization id"),
fieldWithPath("departmentId").description("Employee's department id"),
fieldWithPath("name").description("Employee's name"),
fieldWithPath("age").description("Employee's age"),
fieldWithPath("position").description("Employee's position inside organization")
)));
}
@Test
public void findPersonById() throws JsonProcessingException, Exception {
this.mockMvc.perform(get("/{id}", 1).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("{method-name}/", responseFields(
fieldWithPath("id").description("Employee id"),
fieldWithPath("organizationId").description("Employee's organization id"),
fieldWithPath("departmentId").description("Employee's department id"),
fieldWithPath("name").description("Employee's name"),
fieldWithPath("age").description("Employee's age"),
fieldWithPath("position").description("Employee's position inside organization")
), pathParameters(parameterWithName("id").description("Employee id"))));
}
}
If you would like to customize some settings of Spring REST Docs, you should provide @TestConfiguration
class inside the JUnit test class. In the following code fragment, you may see an example of such customization. I overrode default snippets output directory from index
to test method-specific name and forced the generation of sample request and responses using the prettyPrint
option (single parameter in the separated line).
@TestConfiguration
static class CustomizationConfiguration implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.operationPreprocessors()
.withRequestDefaults(prettyPrint())
.withResponseDefaults(prettyPrint());
}
@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}
}
Now, if you execute the mvn clean install
on your project, you should see the following structure inside your output directory.
4. Viewing and Publishing API Docs
Once we have successfully built our project, the documentation has been generated. We can display an HTML file available at target/generated-docs/api.html
. It provides the full documentation of our API.
And, the next part:
You may also want to publish this inside your application JAR file. If you configure the maven-resources-plugin
, the following example is visible and would be available under the /static/docs directory inside JAR.
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.outputDirectory}/static/docs
</outputDirectory>
<resources>
<resource>
<directory>
${project.build.directory}/generated-docs
</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Conclusion
That's it for this post! The sample service generating documentation using Spring REST Docs is available on GitHub under repository https://github.com/piomin/sample-spring-microservices-new/tree/rest-api-docs/employee-service. I’m not sure that Swagger and Spring REST Docs should be treated as competitive solutions. I use Swagger for the simple testing of an API on the running application or exposing the specification that can be used for the automated generation of a client code. Spring REST Docs can be used for generating documentation that can be published somewhere and “is accurate, concise, and well-structured. This documentation then allows your users to get the information they need with a minimum of fuss." I think there are no obstacles to using Spring REST Docs and SpringFox Swagger together in your project to provide the most valuable documentation of API exposed in your application.
Published at DZone with permission of Piotr Mińkowski, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments