Migration from Swagger to open-api with Spring boot 3

Dinakaran Ramadurai
3 min readFeb 11, 2023

If you are using Swagger-UI, it is time to switch to Spring Doc as swagger is a dead project. Swagger hasn’t had a release since July 2020. Even though it worked with spring boot 2.x, it won't work well for spring boot 3 as it has many breaking changes.

Migrating from swagger to open-api

What is SpringDoc-OpenAPI?

springdoc-openapi java library helps to automate the generation of API documentation using spring boot projects. Automatically generates documentation in JSON/YAML and HTML format APIs. This documentation can be completed by comments using swagger-api annotations.

Dependencies

Remove spring fox and swagger 2 dependencies. Add springdoc-openapi-ui dependency based on application type web MVC or web flux

<!-- provides springdoc-openapi-ui -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>2.0.2</version>
</dependency>

Replace swagger 2 annotations with swagger 3 annotations (it is already included with springdoc-openapi-ui dependency). Package for swagger 3 annotations is io.swagger.v3.oas.annotations.

@Api → @Tag
@ApiIgnore → @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden
@ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
@ApiParam → @Parameter

Configurations

API Info details — In swagger

//API info
private ApiInfo apiInfo() {
return new ApiInfo("Micro service", "APIs for Test Console service", "1.0",
"Terms of service",
new Contact("Dev Team", "https://github.com", "dinakar14.ram@gmail.com"), "License of API",
"API license URL", Collections.emptyList());
}

In Spring Doc open-api

@Bean
public OpenAPI springOpenAPI() {
return new OpenAPI()
.info(new Info().title("Micro service").description("APIs for Test Console service").version("1.0")
.license(new License().name("Dev Team").url("https://github.com")))
.externalDocs(new ExternalDocumentation().description("Test Documentation").url("https://github.com"));
}

If you have multiple Docket beans replace them with GroupedOpenApi beans. In Swagger

@Bean
public Docket publicApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.dina.openapi.controller"))
.paths(PathSelectors.ant("/api/**"))
.build()
.groupName("test-api")
.apiInfo(apiInfo());
}

In Spring Doc open-api

@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder().group("test-api").pathsToMatch("/api/**").build();
}

If you have only one docket, you can add the below in the application.properties

springdoc.packagesToScan=com.dina.openapi.controller
springdoc.pathsToMatch=/api/**

Authorization

In Swagger

@Bean
public Docket api() {

return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.securityContexts(Arrays.asList(securityContext())).securitySchemes(Arrays.asList(apiKey())).select()
.apis(RequestHandlerSelectors.basePackage("com.dina.openapi.controller"))
.paths(PathSelectors.ant("/api/**")).build();
}

private ApiKey apiKey() {
return new ApiKey("apiKey", "Authorization", "header");
}

private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).build();
}

private List<SecurityReference> defaultAuth() {
return Arrays.asList(new SecurityReference("apiKey", new AuthorizationScope[0]));
}

In spring Doc open api

@Bean
public OpenAPI customizeOpenAPI() {
final String securitySchemeName = "bearerAuth";
return new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
.components(
new Components()
.addSecuritySchemes(securitySchemeName,
new SecurityScheme()
.name(securitySchemeName)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
)
);
}

Global Headers

In swagger

@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.securityContexts(Arrays.asList(securityContext())).securitySchemes(Arrays.asList(apiKey())).select()
.apis(RequestHandlerSelectors.basePackage("com.dina.openapi.controller"))
.paths(PathSelectors.ant("/api/**")).build()
.globalRequestParameters(Arrays.asList(new RequestParameterBuilder().name("test-header")
.description("Test Header").in(ParameterType.HEADER).required(true)
.query(simpleParameterSpecificationBuilder -> simpleParameterSpecificationBuilder
.allowEmptyValue(false).model(modelSpecificationBuilder -> modelSpecificationBuilder
.scalarModel(ScalarType.STRING)))
.build()));
}

In Spring Doc open-api, you have to handle the duplicate in parameter list so that headers doesn’t appear twice. This issue happens since open-api scans the controller class, adds the request header into parameter list and shows them in /swagger-ui.html. when you add the same header as global header, since it is a list it would appear twice. It is handled automatically in swagger but in open-api we have to handle it.

public static final String TEST_HEADER = "test-header";

@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder().group("test-api").pathsToMatch("/api/**").build();
}

@Bean
public OperationCustomizer customGlobalHeaders() {

return (Operation operation, HandlerMethod handlerMethod) -> {
Optional<List<Parameter>> isParameterPresent = Optional.ofNullable(operation.getParameters());
Boolean isTestHeaderPresent = Boolean.FALSE;
if (isParameterPresent.isPresent()) {
isTestHeaderPresent = isParameterPresent.get().stream()
.anyMatch(param -> param.getName().equalsIgnoreCase(TEST_HEADER));
}
if (Boolean.FALSE.equals(isTestHeaderPresent)) {
Parameter remoteUser = new Parameter().in(ParameterIn.HEADER.toString()).schema(new StringSchema())
.name(TEST_HEADER).description("Test Header").required(true);
operation.addParametersItem(remoteUser);
}
return operation;
};
}

Deploying behind Proxy like Spring cloud gateway

If you deploy your micro service that has Spring Doc open-api behind reverse proxy like spring cloud gateway, you have to add the following in application.properties

server.forward-headers-strategy=framework

This is because you request might change along the way. Your application may be running on 11.11.11.11:8080, but HTTP clients should only see github.org. Forward Headersdefines the Forwarded HTTP header; proxies can use this header to provide information about the original request

Conclusion

To access the spring doc open-api UI type the following in browser— http://localhost:8080/swagger-ui/index.html

If you are still using swagger, it is going to die migrate to Spring Doc open-api.

As always. you can check the code here at — https://github.com/dinakaranram9/open-api.git

--

--

Dinakaran Ramadurai

Software Engineer who solves problems and helping others with same problem