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
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
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
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:
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:
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.
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:
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.
val swagger = ApiBrowser.swaggerUi()
.url("https://my.company.com/api/openapi.json")
Host.create()
.handler(swagger)
.defaults()
.run()