Migration from 6.x to 7.0
Javalin OpenAPI 7.0 is a major release built around OpenAPI 3.1.0 and a completely redesigned core. Below you'll find what's new and what you need to change.
| 6.x | 7.0 | |
|---|---|---|
| Java | 11+ | 17+ |
| Kotlin | 1.9 | 2.3 |
| Javalin | 6.x | 7.x |
What's New
OpenAPI 3.1.0 & JSON Schema 2020-12
Generated specs are now OpenAPI 3.1.0 (was 3.0.3), fully aligned with JSON Schema 2020-12. See Breaking Changes for details on how this affects generated output.
Runtime Builder DSL
The new openapi-generator module provides a Kotlin DSL for building or extending OpenAPI specifications programmatically at runtime. Define entirely new operations or modify existing ones generated by the annotation processor:
spec.path("/users/{id}").operation("get") {
tags("users")
summary("Get user by ID")
parameters {
parameter("id", "path", required = true) { type("string") }
}
responses {
response("200") {
description("OK")
content {
mediaType("application/json") {
schema { ref("#/components/schemas/User") }
}
}
}
response("404") { description("User not found") }
}
}See Runtime Builder DSL for details.
Naming Strategies
New @OpenApiNaming annotation applies automatic name transformation to properties and enum values at the class level:
@OpenApiNaming(OpenApiNamingStrategy.SNAKE_CASE)
class UserResponse(
val firstName: String, // → "first_name"
val lastName: String, // → "last_name"
)See Naming Strategies for details.
Enhanced Enum Support
Enums now support @OpenApiPropertyType, @OpenApiDescription, and @OpenApiName annotations. Integer enums can be modeled with @OpenApiPropertyType(definedBy = Int::class) combined with @OpenApiName:
@OpenApiPropertyType(definedBy = Int::class)
@OpenApiDescription("Sort order:\n * 1 - Ascending\n * 2 - Descending")
enum class SortOrder {
@OpenApiName("1") ASCENDING,
@OpenApiName("2") DESCENDING
}See Enums for details.
Raw Examples
@OpenApiExample now supports a raw field for JSON-formatted examples (previously only available on @OpenApiExampleProperty):
@OpenApiExample(raw = """{"key": "value"}""")See Examples for details.
Additional Type Mappings
New built-in type mappings:
| Java Type | OpenAPI Type | Format |
|---|---|---|
UUID | string | uuid |
OffsetDateTime | string | date-time |
LocalTime | string | time |
Duration | string | duration |
URI | string | uri |
URL | string | uri |
Simplified Plugin API
Security methods are available directly on OpenApiSchemaBuilder — no more withSecurity { ... } wrapper:
openApiConfig.withDefinitionConfiguration { version, builder ->
builder
.info { it.title("My API") }
.withBearerAuth()
.withGlobalSecurity("BearerAuth")
}withDefinitionProcessor is now a top-level plugin option. Both SwaggerConfiguration and ReDocConfiguration use fluent with*() methods matching OpenApiPluginConfiguration style.
UI Updates
| UI | 6.x | 7.0 |
|---|---|---|
| Swagger UI | 5.17.14 | 5.31.2 |
| ReDoc | 2.1.4 | 2.5.0 |
Documentation
New documentation site covering all features: setup guides, OpenAPI annotations, JSON Schema generation, type composition, validation, naming strategies, enums, examples, and the runtime builder DSL. You're reading it right now.
Breaking Changes
Dependencies
Update the version in your build file:
val openapi = "7.0.0" // was 6.x<version>7.0.0</version> <!-- was 6.x -->New openapi-generator module
7.0 extracted the runtime builder DSL into a new openapi-generator module. This is a transitive dependency of openapi-annotation-processor and all Javalin plugins, so no changes to your dependency declarations are needed unless you were importing internal generator classes directly.
Gson removed
The openapi-specification module no longer depends on Gson. If your project relied on the transitive Gson dependency, add it explicitly:
implementation("com.google.code.gson:gson:2.11.0")Jackson (jackson-databind, jackson-module-kotlin) remains the default and only JSON library used internally.
Groovy configuration script path
6.x auto-detected openapi.groovy from the source tree. 7.0 requires an explicit annotation processor option:
tasks.withType<JavaCompile> {
options.compilerArgs.add(
"-Aopenapi.groovy.path=$projectDir/src/main/compile/openapi.groovy"
)
}kapt {
arguments {
arg("openapi.groovy.path", "$projectDir/src/main/compile/openapi.groovy")
}
}<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.javalin.community.openapi</groupId>
<artifactId>openapi-annotation-processor</artifactId>
<version>7.0.0</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Aopenapi.groovy.path=${project.basedir}/src/main/compile/openapi.groovy</arg>
</compilerArgs>
</configuration>
</plugin>If you don't use a Groovy configuration script, no changes are needed.
OpenAPI 3.0.3 → 3.1.0
Generated specs are now OpenAPI 3.1.0 (was 3.0.3), based on JSON Schema 2020-12. This changes the generated output in several ways.
Nullable types
The nullable: true keyword is replaced by type arrays. This affects all nullable properties in your generated schemas:
// 7.0 (OpenAPI 3.1.0)
{ "type": ["string", "null"] }
// 6.x (OpenAPI 3.0.3)
{ "type": "string", "nullable": true }Nullable $ref types are wrapped in anyOf:
// 7.0
{
"anyOf": [
{ "$ref": "#/components/schemas/Address" },
{ "type": "null" }
]
}
// 6.x
{ "$ref": "#/components/schemas/Address", "nullable": true }Nullable oneOf/anyOf types append a null entry:
// 7.0 — oneOf with nullable
{
"oneOf": [
{ "$ref": "#/components/schemas/Cat" },
{ "$ref": "#/components/schemas/Dog" },
{ "type": "null" }
]
}No annotation changes needed — this is handled automatically by the generator. If you have tooling that parses the generated spec (validators, code generators, etc.), it needs to understand 3.1.0 nullable semantics.
additionalProperties default
6.x explicitly set "additionalProperties": false on every object schema. 7.0 omits it, following JSON Schema 2020-12 where the default is true. If your consumers relied on strict additionalProperties: false, use a definition processor to re-add it.
exclusiveMinimum / exclusiveMaximum
Changed from Boolean flags to numeric String values, matching JSON Schema 2020-12:
// 7.0 — numeric value (the exclusive bound itself)
@OpenApiNumberValidation(exclusiveMinimum = "0")
// 6.x — boolean flag (means: minimum is exclusive)
@OpenApiNumberValidation(minimum = "0", exclusiveMinimum = true)Parameter examples
Parameter-level examples are now emitted at the parameter level (per spec), not nested inside the schema:
// 7.0
{ "name": "id", "in": "path", "schema": { "type": "string" }, "example": "abc" }
// 6.x
{ "name": "id", "in": "path", "schema": { "type": "string", "example": "abc" } }Plugin configuration
definitionConfiguration API replaced
The DefinitionConfiguration / SecurityComponentConfiguration classes are removed. The withDefinitionConfiguration callback now receives an OpenApiSchemaBuilder directly:
// 7.0
openApiConfig.withDefinitionConfiguration { version, builder ->
builder
.info { it.title("My API") }
.server { it.url("https://api.example.com") }
.withBearerAuth()
.withGlobalSecurity("BearerAuth")
}
// Definition processor is now a separate top-level option:
openApiConfig.withDefinitionProcessor { content ->
content.set<TextNode>("x-custom", TextNode("value"))
content.toPrettyString()
}
// 6.x
openApiConfig.withDefinitionConfiguration { version, definition ->
definition
.withInfo { it.title("My API") }
.withServer { it.url("https://api.example.com") }
.withSecurity { security ->
security
.withBearerAuth()
.withGlobalSecurity("BearerAuth")
}
.withDefinitionProcessor { content ->
content.set<TextNode>("x-custom", TextNode("value"))
content.toPrettyString()
}
}Key differences:
withInfo(...)→info(...)withServer(...)→server(...)- Security methods (
withBearerAuth,withBasicAuth,withOAuth2, etc.) moved fromSecurityComponentConfigurationtoOpenApiSchemaBuilderdirectly — no morewithSecurity(security -> ...)wrapper withDefinitionProcessormoved fromDefinitionConfigurationtoOpenApiPluginConfiguration
Swagger & ReDoc configuration style
Both SwaggerConfiguration and ReDocConfiguration now use @JvmField constructor parameters and fluent with*() methods, matching the OpenApiPluginConfiguration style.
Direct property assignment still works identically in Kotlin. Java users should replace setX() setters with withX() fluent methods or direct field access:
// Works in both 6.x and 7.0
config.registerPlugin(SwaggerPlugin {
it.documentationPath = "/api/docs"
it.uiPath = "/swagger"
})The same applies to ReDocConfiguration.
Annotation changes
formParams removed
The formParams field was removed from @OpenApi. Use requestBody with the appropriate content type instead:
// 7.0
@OpenApi(
requestBody = OpenApiRequestBody(
content = [OpenApiContent(from = File::class, mimeType = "multipart/form-data")]
)
)
// 6.x
@OpenApi(
formParams = [OpenApiParam(name = "file", type = File::class)]
)Full before/after example
Javalin.start { config ->
config.registerPlugin(OpenApiPlugin { openapi ->
openapi
.withDocumentationPath("/openapi")
.withRoles(Rules.ANONYMOUS)
.withDefinitionConfiguration { version, builder ->
builder
.info { it.title("My API").description("API docs") }
.server { it.url("https://api.example.com") }
.withBearerAuth()
.withGlobalSecurity("BearerAuth")
}
.withDefinitionProcessor { content ->
content.set<TextNode>("x-gen", TextNode("true"))
content.toPrettyString()
}
})
config.registerPlugin(SwaggerPlugin { it.documentationPath = "/openapi" })
config.registerPlugin(ReDocPlugin { it.documentationPath = "/openapi" })
}Javalin.createAndStart { config ->
config.registerPlugin(OpenApiPlugin { openapi ->
openapi
.withDocumentationPath("/openapi")
.withRoles(Rules.ANONYMOUS)
.withDefinitionConfiguration { version, definition ->
definition
.withInfo { it.title("My API").description("API docs") }
.withServer { it.url("https://api.example.com") }
.withSecurity { security ->
security
.withBearerAuth()
.withGlobalSecurity("BearerAuth")
}
.withDefinitionProcessor { content ->
content.set<TextNode>("x-gen", TextNode("true"))
content.toPrettyString()
}
}
})
config.registerPlugin(SwaggerPlugin { it.documentationPath = "/openapi" })
config.registerPlugin(ReDocPlugin { it.documentationPath = "/openapi" })
}