The OpenAPI Specification (OAS) defines
“a standard, programming language-agnostic interface description for REST APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic“.
This post demonstrates how to add OpenAPI specifications (version 3.0.1) to document existing RESTful APIs in a Spring Boot/Spring MVC project. This would be useful when you have an existing application and want to expose its APIs via OpenAPI. I will walk through how to setup the project and update the codes to generate the desired documentation.
Project Setup
I use the springdoc-openapi library to generate the OpenAPI documentation. Setting this up is straight forward, just add the following dependencies in your Maven pom file.
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-core</artifactId> <version>1.1.44</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.2.6</version> </dependency>
Once the above is included, the OpenAPI documentation will be generated for all RESTful APIs in the application and made available at the path /v3/api-docs. For example
http://localhost:8080/v3/api-docs/
The above URL generates the OpenAPI document in json. For yaml format, use the following URL
http://localhost:8080/v3/api-docs.yaml
The above Maven also includes Swagger UI which can be accessed via the URL
http://localhost:8080/swagger-ui.html
Springdoc OpenAPI Configuration
Now we have added the springdoc library, we can start customize our API codes to generate the OpenAPI documentation.
General API Info & Servers
General information about the APIs and server URLs are configured under the @OpenAPIDefinition annotation. This is used to provide general metadata information about the APIs such as version, contact and licencing.
@OpenAPIDefinition( info = @Info( title = "Order API", version = "v2", description = "This app provides REST APIs for order entities", contact = @Contact( name = "Raymond", email = "admin@orderapi.com", url = "http://orderapi.com/support" ) ), ), servers = { @Server( url="<dev url>", description="DEV Server" ), @Server( url="<prod url>", description="PROD Server" ) } ) public class OpenApiConfig { }
The corresponding OpenAPI document for the above annotation is shown below
openapi: 3.0.1 info: title: Order API description: This app provides REST APIs for order entities contact: name: Raymond url: http://orderapi.com/support email: admin@orderapi.com version: v2 servers: - url: <dev url> description: DEV Server variables: {} - url: <prod url> description: PROD Server variables: {}
Security
There are two steps for defining API security in OpenAPI.
First, define a global security schema. For example, an OAuth2 schema using JWT can be defined using the @SecurityScheme annotation below:
@SecurityScheme( name = "myOauth2Security", type = SecuritySchemeType.OAUTH2, in = SecuritySchemeIn.HEADER, bearerFormat = "jwt", flows = @OAuthFlows( implicit = @OAuthFlow( authorizationUrl = "http://url.com/auth", scopes = { @OAuthScope(name = "read", description = "read access"), @OAuthScope(name = "write", description = "Write access") } ) ) )
Now that a global security schema has been defined, we can apply it to any API by annotating the method in the REST controller with @Operation annotation and specify the security with @SecurityRequirement annotation by referring to the schema above by its name.
@RestController public class OrderController { ... @Operation( security = @SecurityRequirement(name = "myOauth2Security", scopes = "write"), // ... other attributes ) @PostMapping(value = "/order", consumes = MediaType.APPLICATION_JSON_VALUE) public void postOrder(OrderDto order) { // Implementation codes } ...
Paths & Operations
Springdoc is smart enough to interpret the path of an endpoint from its Spring MVC request mapping annotation, i.e. @PostMapping, @GetMapping etc.
To provide more information about an endpoint, use @Operation annotation as shown in the previous section. Below is a complete example for an endpoint to post a new order to the backend server:
@RestController public class OrderController { ... @Operation( summary = "Create a new order", description = "Use this endpoint to create a new order in the backend", security = @SecurityRequirement(name = "myOauth2Security", scopes = "write"), responses= { @ApiResponse(responseCode = "201", description = "Success"), @ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "500", description = "Server Error") } ) @ResponseStatus(code = HttpStatus.CREATED) @PostMapping(value = "/order", consumes = MediaType.APPLICATION_JSON_VALUE) public void postOrder(OrderDto order) { // Implementation codes } ...
Note we provide here a brief summary and description of the endpoint. Also, a number of possible responses and their reasons are included using the @ApiResponse annotation in the responses attribute. The above annotation will result in the following OpenAPI fragment
/order: post: summary: Create a new order description: Use this endpoint to create a new order in the backend operationId: postOrder requestBody: content: application/json: schema: $ref: '#/components/schemas/OrderDto' responses: 201: description: Success 403: description: Forbidden 500: description: Server Error security: - myOauth2Security: - write
Components
The above API expects an OrderDto object in the request. Springdoc will automatically generate the component element of the object in OpenAPI. It will include all the object properties and their data types. To add more description of the object, we can use the @Schema annotation.
@Schema( name = "orderDto", description = "Data object for an order", oneOf = OrderDto.class ) public class OrderDto { ... }
Springdoc also supports JSR-303. For example
public class OrderDto { private Long id; @NotNull private String orderName; @NotNull private String customerName; @NotNull private String customerAddress; private BigDecimal orderAmount; @NotNull private List<OrderLineDto> lines; ...
The above will generate the following OpenAPI fragment
components: schemas: orderDto: required: - customerAddress - customerName - lines - orderName type: object description: Data object for an order properties: id: type: integer format: int64 orderName: type: string customerName: type: string customerAddress: type: string orderAmount: type: number lines: type: array items: $ref: '#/components/schemas/OrderLineDto'
Note the required fields, marked by @NotNull, are defined in the required: elements.
Conclusions
This post has demonstrated how to update a Spring Boot / MVC project to automatically generate OpenAPI specification for the application’s API endpoints. The OpenAPI document can be used by human and computer, for example to generate client codes to consume the API using tools such as the OpenAPI Generator.
For more detailed information about OpenAPI, refer to specification here. The Swagger site also has a section which explains in details the basic structure of an OpenAPI document as well as its individual elements.