API Browsing

This module adds JavaScript based applications to visualize an Open API definition such as Swagger UI, Redoc or Scalar to your app. All resources required by those apps are hosted locally so no internet connection is required.

The following example creates an API, the corresponding Open API definition and three endpoints to host Swagger UI, Redoc and Scalar.

Webservices

Kotlin
import org.genhttp.engine.internal.Host

import org.genhttp.modules.apibrowsing.*
import org.genhttp.modules.layouting.Layout
import org.genhttp.modules.openapi.addOpenApi
import org.genhttp.modules.practices.defaults
import org.genhttp.modules.reflection.Method
import org.genhttp.modules.webservices.ResourceMethod
import org.genhttp.modules.webservices.addService

suspend fun main() {
    val app = Layout.create()
        .addService<BookService>("books")
        .addOpenApi()
        .addSwaggerUi()
        .addRedoc()
        .addScalar()

    Host.create()
        .handler(app)
        .defaults()
        .development()
        .run()
}

data class Book(val id: Int, val title: String)

class BookService {

    @ResourceMethod
    fun getBooks(): List<Book> = emptyList()

    @ResourceMethod(Method.Put)
    fun createBook(book: Book): Book = book

    @ResourceMethod(Method.Post)
    fun updateBook(book: Book): Book = book

    @ResourceMethod(Method.Delete)
    fun deleteBook(id: Int) { }

}

Functional

Kotlin
import org.genhttp.engine.internal.Host

import org.genhttp.modules.apibrowsing.*
import org.genhttp.modules.functional.*
import org.genhttp.modules.layouting.Layout
import org.genhttp.modules.openapi.addOpenApi
import org.genhttp.modules.practices.defaults

suspend fun main() {
    val bookApi = Inline.create()
        .get { emptyList<Book>() }
        .put { book: Book -> book }
        .post { book: Book -> book }
        .delete { id: Int -> }

    val app = Layout.create()
        .add("books", bookApi)
        .addOpenApi()
        .addSwaggerUi()
        .addRedoc()
        .addScalar()

    Host.create()
        .handler(app)
        .defaults()
        .development()
        .run()
}

data class Book(val id: Int, val title: String)

Controllers

Kotlin
import org.genhttp.engine.internal.Host

import org.genhttp.modules.apibrowsing.*
import org.genhttp.modules.controllers.ControllerAction
import org.genhttp.modules.controllers.addController
import org.genhttp.modules.layouting.Layout
import org.genhttp.modules.openapi.addOpenApi
import org.genhttp.modules.practices.defaults
import org.genhttp.modules.reflection.Method

suspend fun main() {
    val app = Layout.create()
        .addController<BookController>("books")
        .addOpenApi()
        .addSwaggerUi()
        .addRedoc()
        .addScalar()

    Host.create()
        .handler(app)
        .defaults()
        .development()
        .run()
}

data class Book(val id: Int, val title: String)

class BookController {

    @ControllerAction(Method.Get)
    fun list(): List<Book> = emptyList()

    @ControllerAction(Method.Put)
    fun create(book: Book): Book = book

    @ControllerAction(Method.Post)
    fun update(book: Book): Book = book

    @ControllerAction(Method.Delete)
    fun delete(id: Int) { }

}

After running this sample you can view http://localhost:8080/swagger/, http://localhost:8080/redoc/ and http://localhost:8080/scalar/ in your browser.

Referencing a Definition

By default, the applications will use ../openapi.json to reference to the Open API definition to be used. There is no magic link between the API browser and the Open API handler, so you need to pass the correct link depending on the structure of your application. In the following example, docs and the API are separated so another path is required and needs to be passed:

Kotlin
data class Book(val id: Int, val title: String)

val bookApi = Inline.create()
    .get { emptyList<Book>() }
    .put { book: Book -> book }
    .post { book: Book -> book }
    .delete { id: Int -> }

val api = Layout.create()
    .add("books", bookApi)
    .addOpenApi()

val app = Layout.create()
    .add(arrayOf("api", "v1"), api)
    .addSwaggerUi(url = "../api/v1/openapi.json")

While you can pass an absolute URL, we recommend passing a relative URL to avoid issues with CORS and your reverse proxy setup.

Adjusting the Path

By default, the applications will be registered at /swagger/, /redoc/ or /scalar/, relative to the path of the current layout they are added to. This path can be adjusted as needed:

Kotlin
val app = Layout.create()
    .addSwaggerUi(segment = "docs")

Adjusting Meta Data

There are parameters to adjust the meta data of the generated HTML index file. Currently, only the title can be changed.

Kotlin
val app = Layout.create()
    .addSwaggerUi(title = "Book API")

Hiding in Production

You might want use this feature only during development and disable it in production. There is no built-in way to achieve this, but you can add the application conditionally, e.g. depending on an environment variable or a system property:

Kotlin
val app = Layout.create()
    .addOpenApi()

if (System.getenv("APP_ENV") != "production") {
    app.addSwaggerUi()
}

Advanced Usage

The examples above show the typical use case of adding an API browser to a layout. For full access to the handlers, you may also directly create and link them.

Kotlin
val swagger = ApiBrowser.swaggerUi()
    .url("https://my.company.com/api/openapi.json")

Host.create()
    .handler(swagger)
    .defaults()
    .run()