Should I Use Spring REST Docs or OpenAPI?
1 CommentLast Updated on November 27, 2019 by jt
Should I Use Spring REST Docs or OpenAPI?
Recently, I was asked which is better to use. Spring REST Docs or OpenAPI.
From the perspective of generating documentation for your APIs, you may think these two options are effectively the same thing.
But, it’s not exactly an apples to apples comparison.
These are significantly different options.
Spring REST Docs is a tool which helps you create API documentation from your controller tests. By default, the output of Spring REST Docs is plain text via Asciidoctor. This output is used to generate API documentation.
In contrast, OpenAPI is a formal specification for APIs. The specification is backed by a JSON schema, which used to fully describe APIs.
OpenAPI Specifications are written in JSON or YAML. The specification can then be programmatically parsed to produce rich API documentation.
When you create an API, having complete and accurate documentation is very important.
Both Spring REST Docs and OpenAPI can produce accurate documentation for your APIs.
Let’s take a closer look at how each accomplishes this.
Spring REST Docs
The official documentation for Spring REST Docs is here.
Spring Boot Test Configuration
Setting up a Spring Boot test to use Spring REST Docs is fairly simple.
Example:
@ExtendWith(RestDocumentationExtension.class) @AutoConfigureRestDocs @WebMvcTest(BeerController.class) public class BeerControllerTest {
In the above, you need to annotate the test with @ExtendWith(RestDocumentationExtension.class)
which is a JUnit 5 extension for Spring REST Docs. And @AutoConfigureRestDocs
which will provide you an auto configured instance of Spring MockMVC for the test.
Example JUnit 5 Test
Here is an example of a test using Spring REST Docs.
@Test public void getBeer() throws Exception { given(beerService.getBeerById(any(UUID.class))).willReturn(validBeer); ConstrainedFields fields = new ConstrainedFields(BeerDto.class); mockMvc.perform(get("/api/v1/beer/{beerId}", validBeer.getId().toString()).accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(jsonPath("$.id", is(validBeer.getId().toString()))) .andExpect(jsonPath("$.beerName", is("Beer1"))) .andDo(document("v1/beer-get", pathParameters ( parameterWithName("beerId").description("UUID of desired beer to get.") ), responseFields( fields.withPath("id").description("Id of Beer").type(UUID.class), fields.withPath("createdDate").description("Date Created").type(OffsetDateTime.class), fields.withPath("lastUpdatedDate").description("Date Updated").type(OffsetDateTime.class), fields.withPath("beerName").description("Beer Name"), fields.withPath("beerStyle").description("Beer Style"), fields.withPath("upc").description("UPC of Beer") ))); }
The test above will create several asciidoc snippets.
Here is an example of a generated snippet.
|=== |Path|Type|Description |`+id+` |`+class java.util.UUID+` |Id of Beer |`+createdDate+` |`+class java.time.OffsetDateTime+` |Date Created |`+lastUpdatedDate+` |`+class java.time.OffsetDateTime+` |Date Updated |`+beerName+` |`+String+` |Beer Name |`+beerStyle+` |`+String+` |Beer Style |`+upc+` |`+Number+` |UPC of Beer |===
Through asciidoc configuration, these snippets can be configured into a rich HTML document. (See Spring documentation for details.)
This document can be published for your API users.
For a complete working example, please see this GitHub repo (note branch!).
OpenAPI
The approach of OpenAPI is significantly different.
OpenAPI is a formal specification. You can see details of the 3.0.2 version here.
This is significantly different from Spring REST Docs.
The OpenAPI Specification is a very popular open source project with broad support. It is a technology standard, and is not Spring specific.
The Get Beer operation tested above could be defined in OpenAPI as:
/api/v1/beer/{beerId}: parameters: - $ref: "#/components/parameters/BeerId" - $ref: '#/components/parameters/ShowInventoryOnHand' get: description: Get Beer by Id tags: - Beer Service responses: '200': description: Get Beer by id Response content: application/json: schema: $ref: '#/components/schemas/Beer' '404': description: Not Found
This is just the snippet of the operation. The complete OpenAPI specification can be found here.
If you’re new to OpenAPI, I have a complete course called OpenAPI: Beginner to Guru to quickly get you up to speed writing your own specifications.
Generating User Documentation with OpenAPI
OpenAPI specifications are written in JSON or YAML.
This is great for computers. Not so much for people.
OpenAPI has a very large community of Open Source tools. These are tools which can read specifications to produce an output. The output can be documentation, client SDKs, Server stubbs, Mock Servers, Testing tools, and much more.
There is a whole segment of tools for generating end user API documentation from OpenAPI Specifications.
ReDoc API Documentation Example
One of my favorite documentation generators is ReDoc. ReDoc uses React to provide a rich single page application experience.
Here is a screenshot example of ReDoc.
You can see a complete, working example hosted on GitHub Pages here.
Testing with OpenAPI
You may be thinking a clear gap between Spring REST Docs and OpenAPI is testing.
With Spring REST Docs, the documentation is driven from controller tests. This gives you confidence that the generated documentation is correct.
By using Atlassian’s Swagger Request validator, we can validate every request / response against an OpenAPI specification.
Here is an example JUnit 5 test with Spring MockMVC.
public static final String OAC_SPEC = "https://raw.githubusercontent.com/sfg-beer-works/brewery-api/master/spec/openapi.yaml"; @Test void getBeerById() throws Exception { //given DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"); given(beerService.findBeerById(any(UUID.class), anyBoolean())).willReturn(validReturnBeer); mockMvc.perform(get("/api/v1/beer/" + UUID.randomUUID()).accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.beerName", is("Beer1"))) .andExpect(jsonPath("$.createdDate").isNotEmpty()) .andExpect(openApi().isValid(OAC_SPEC)); }
Here you can see how easy it is to assert your controller test matches the OpenAPI specification.
The last line:
.andExpect(openApi().isValid(OAC_SPEC));
Is the code which invokes validations from Atlassian’s Swagger Request Validator.
The complete example is available here in GitHub.
Conclusion
I’ve used Spring REST Docs for some time. It does get the job of creating API documentation done.
However, writing tests becomes very verbose when you wish to have detailed documentation.
The asciidoc process always feels like a bit of a kludge to me.
Yes it works. But, can get overly complicated.
Also, the output of Spring REST Docs also feels closed. You get a HTML document. It is unique to your APIs and configuration.
In contrast, with OpenAPI, you are starting with a widely accepted standard and a thriving open source community.
From an OpenAPI Specification you can:
- Create End user API Documentation
- Generate client side code in over 50 languages
- Generate server side code in over 40 languages
- Create Mock servers
- Import directly into Postman for API testing
- Define Consumer Driven Contracts
- Generate tests
- Integrate specification validation into your tests
- And more!
Which to Use?
If you’re at a Java shop, and writing API documentation for internal use, Spring REST Docs is fine.
I suggest strongly considering using OpenAPI if:
- You have public facing APIs
- Are in a large corporation with a diverse technology base
- Are using a microservice architecture
Learn more about writing OpenAPI Specifications here!
Abhishek
This is an awesome article and thank your putting it together
I am trying to do similar thing with RestAssuredMockMvc but haven’t been successful. The swagger-request-validator library has a separate module for the RestAssured using filters but RestAssuredMockMvc doesn’t have filters.
Do you know if there is another way I can make this work?