I'm working on a Javalin project where I'm trying to document my REST API using OpenAPI annotations. Despite annotating my controller method with @OpenApi, I'm encountering a "no api definition provided" error when trying to access the generated documentation via Swagger UI or ReDoc.
Here's the relevant portion of my Kotlin code where I set up Javalin, configure the OpenAPI plugin, and define a simple UserController:
package org.example
import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.*
import io.javalin.http.Context
import io.javalin.openapi.*
import io.javalin.openapi.plugin.OpenApiPlugin
import io.javalin.openapi.plugin.redoc.ReDocPlugin
import io.javalin.openapi.plugin.swagger.SwaggerPlugin
import java.util.concurrent.atomic.AtomicInteger
data class User(
val id: Int,
val name: String,
val email: String
)
object UserService {
private val users = hashMapOf(
0 to User(id = 0, name = "Alice", email = "alice@alice.kt"),
1 to User(id = 1, name = "Bob", email = "bob@bob.kt"),
2 to User(id = 2, name = "Carol", email = "carol@carol.kt"),
3 to User(id = 3, name = "Dave", email = "dave@dave.kt")
)
private var lastId: AtomicInteger = AtomicInteger(users.size - 1)
fun getAll() = users.values
}
fun getConfiguredOpenApiPlugin(): OpenApiPlugin {
return OpenApiPlugin { pluginConfig ->
pluginConfig.withDefinitionConfiguration { _, definition ->
definition.withInfo { info: OpenApiInfo ->
info.title = "sentinel"
info.version = "1.0.0"
info.description = "sentinel"
}
}
pluginConfig.documentationPath = "/swagger-docs"
}
}
object UserController {
@OpenApi(
summary = "Get all users",
operationId = "getAllUsers",
tags = ["User"],
responses = [OpenApiResponse("200", [OpenApiContent(Array<User>::class)])],
path = "/users",
methods = [HttpMethod.GET]
)
fun getAll(ctx: Context) {
ctx.json(UserService.getAll())
}
}
fun main() {
Javalin.create { config ->
config.registerPlugin(OpenApiPlugin { pluginConfig ->
pluginConfig.withDefinitionConfiguration { version, definition ->
definition.withInfo { info: OpenApiInfo ->
info.title = "Javalin OpenAPI example"
}
}
})
config.registerPlugin(SwaggerPlugin())
config.registerPlugin(ReDocPlugin())
config.router.apiBuilder {
path("users") {
get(UserController::getAll);
}
}
}.start(7001)
println("Check out ReDoc docs at http://localhost:7001/redoc")
println("Check out Swagger UI docs at http://localhost:7001/swagger")
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>untitled</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
<kotlin.version>1.9.22</kotlin.version>
<kotlin.code.style>official</kotlin.code.style>
<javalin.version>6.1.0</javalin.version>
</properties>
<dependencies>
<!-- Kotlin -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-common</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<!-- Javalin -->
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin-bundle</artifactId>
<version>${javalin.version}</version>
</dependency>
<dependency>
<groupId>io.javalin.community.openapi</groupId>
<artifactId>javalin-openapi-plugin</artifactId>
<version>${javalin.version}</version>
</dependency>
<dependency>
<groupId>io.javalin.community.openapi</groupId>
<artifactId>javalin-swagger-plugin</artifactId>
<version>${javalin.version}</version>
</dependency>
<dependency>
<groupId>io.javalin.community.openapi</groupId>
<artifactId>javalin-redoc-plugin</artifactId>
<version>${javalin.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>11</jvmTarget>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>11</source>
<target>11</target>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>io.javalin.community.openapi</groupId>
<artifactId>openapi-annotation-processor</artifactId>
<version>6.0.0-SNAPSHOT</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.1.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<generatorName>kotlin</generatorName>
<inputSpec>${project.basedir}/src/main/resources/openapi.json</inputSpec>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Here is the error:
I faced a similar issue in Javalin 5. What a did was extract the open api config to an auxiliary variable and then register the plugin.
For example:
Javalin.create { config ->
... // other code/configs
val openApiConfig =
OpenApiPluginConfiguration()
.withDocumentationPath("/openapi")
.withDefinitionConfiguration { version, openApiDefinition ->
openApiDefinition
.withOpenApiInfo { openApiInfo ->
openApiInfo.description = "App description goes right here"
openApiInfo.contact =
OpenApiContact().apply {
name = "API Support"
url = "https://www.example.com/support"
email = "support@example.com"
}
}
.withServer { openApiServer ->
openApiServer.description
openApiServer.url
openApiServer.addVariable(
"port",
OpenApiServerVariable().apply {
description = "Server's port"
default = "8080"
},
)
}
}
config.plugins.register(OpenApiPlugin(openApiConfig))
config.plugins.register(SwaggerPlugin())
...
}
Also, in my case, I had to import related dependencies for Javalin 5:
kapt "io.javalin.community.openapi:openapi-annotation-processor:$openapi_version"
implementation "io.javalin.community.openapi:javalin-openapi-plugin:$openapi_version"
implementation "io.javalin.community.openapi:javalin-swagger-plugin:$openapi_version"
Hope you solve it!