Skip to content

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.x7.0
Java11+17+
Kotlin1.92.3
Javalin6.x7.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:

kotlin
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:

kotlin
@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:

kotlin
@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):

kotlin
@OpenApiExample(raw = """{"key": "value"}""")

See Examples for details.

Additional Type Mappings ​

New built-in type mappings:

Java TypeOpenAPI TypeFormat
UUIDstringuuid
OffsetDateTimestringdate-time
LocalTimestringtime
Durationstringduration
URIstringuri
URLstringuri

Simplified Plugin API ​

Security methods are available directly on OpenApiSchemaBuilder — no more withSecurity { ... } wrapper:

kotlin
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 ​

UI6.x7.0
Swagger UI5.17.145.31.2
ReDoc2.1.42.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:

kotlin
val openapi = "7.2.3" // was 6.x
xml
<version>7.2.3</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:

kotlin
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:

kotlin
tasks.withType<JavaCompile> {
    options.compilerArgs.add(
        "-Aopenapi.groovy.path=$projectDir/src/main/compile/openapi.groovy"
    )
}
kotlin
kapt {
    arguments {
        arg("openapi.groovy.path", "$projectDir/src/main/compile/openapi.groovy")
    }
}
xml
<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.2.3</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:

json
// 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:

json
// 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:

json
// 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:

kotlin
// 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:

json
// 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:

kotlin
// 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 from SecurityComponentConfiguration to OpenApiSchemaBuilder directly — no more withSecurity(security -> ...) wrapper
  • withDefinitionProcessor moved from DefinitionConfiguration to OpenApiPluginConfiguration

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:

kotlin
// 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:

kotlin
// 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 ​

kotlin
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" })
}
kotlin
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" })
}