After a bit of searching I achieved this with combination of Web Jars and http4k's static routing.
The potential viewer of the docs must simply visit /docs
path where he gets redirected to /docs/index.html?url=<path to Api description>
where
index.html
is a static Swagger UI entrypoint served from a web jar.
url
query param tells the swagger UI where to fetch the OpenApi description from.
From the DX perspective we have a simple http4k application:
// path the OpenApi description will be exposed on
private const val API_DESCRIPTION_PATH = "/swagger.json"
fun app(): HttpHandler {
val api = contract {
renderer = OpenApi3(ApiInfo("Your API summary", "1.0"), Jackson)
descriptionPath = API_DESCRIPTION_PATH
// the actual API routes
routes += ...
}
return routes(
// the docs routes are not considered part of the API so we define them outside of the contract
swaggerUi(API_DESCRIPTION_PATH),
api
)
}
The swaggerUi
handler implementation follows
/**
* Exposes Swagger UI with /docs path as its entry point.
* @param descriptionPath absolute path to API description JSON. The UI will be configured to fetch it after load.
*/
fun swaggerUi(descriptionPath: String): RoutingHttpHandler = routes(
"docs" bind Method.GET to {
Response(Status.FOUND).header("Location", "/docs/index.html?url=$descriptionPath")
},
// For some reason the static handler does not work without "/" path prefix.
"/docs" bind static(Classpath("META-INF/resources/webjars/swagger-ui/3.25.2"))
)
We also have to include the swagger-ui webjar as our dependency. Here's a Gradle directive:
implementation 'org.webjars:swagger-ui:3.25.2'
See the webjars website for Maven (and more) directives.
Note that the swaggerUi
handler assumes its bound to the /
root path of the whole service. However, that can be easily fixed.