Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expose swagger UI with http4k?

I'm building a microservice with the http4k framework using their Contract APIs. I can easily expose the swagger API description JSON on eg. /swagger.json with

fun app(): HttpHandler = "/" bind contract {
    renderer = OpenApi3(ApiInfo("GoOut Locations API", "1.0"), Jackson)
    descriptionPath = "/swagger.json"
    routes += ...
}

Is there an easy way to expose the swagger UI so that 1) I can specify the path it will be available on. (eg. /swagger-ui) 2) The UI will be preconfigured to fetch the description JSON from the descriptionPath specified above.

An ideal API would look something like

fun app(): HttpHandler = "/" bind contract {
    renderer = OpenApi3(ApiInfo("GoOut Locations API", "1.0"), Jackson)
    descriptionPath = "/swagger.json"
    uiPath = "/swagger-ui"
    routes += ...
}
like image 992
Jen Avatar asked Sep 01 '25 02:09

Jen


1 Answers

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.

like image 75
Jen Avatar answered Sep 02 '25 23:09

Jen