Maven Central NuGet v1.0.0 MIT

OpenAPI to Kotlin Ktor Client Generator

eu.metaengine:metaengine-openapi-kotlin-ktor-maven-plugin

Convert OpenAPI 3.x to Kotlin Ktor HTTP client services and models, delivered as a Maven plugin. Every operation becomes a suspend fun on a Ktor HttpClient with kotlinx.serialization models, bearer/basic auth, retries, timeouts, and error handling — generated straight into your build at generate-sources.

OpenAPI 3.xMetaEngine IRKotlin · Ktor
Open the browser UI for hosted OpenAPI to Kotlin Ktor generation
Install

Pick your registry

The same generator, published to every ecosystem we support. Install however your project expects.

Maven CentralPrimaryeu.metaengine:metaengine-openapi-kotlin-ktor-maven-plugin
$eu.metaengine:metaengine-openapi-kotlin-ktor-maven-plugin
v1.0.0
NuGetMetaEngine.Kotlin.OpenApi.Ktor
$dotnet add package MetaEngine.Kotlin.OpenApi.Ktor
v1.0.0
Usage

Drive it from Maven or programmatically

Maven Central ships a build-time plugin that runs inside your build. NuGet ships the same generator as a C# fluent API — same options, same output.

Maven Central · build-time plugin

Add the plugin to your pom.xml. Its generate goal binds to the generate-sources phase, so the Ktor client and models are produced — and added to your compile roots — on every mvn compile or package. Point <inputSpec> at a spec file or URL and set the target Kotlin <packageName>.

Plugin setup
pom.xml
<build>
  <plugins>
    <plugin>
      <groupId>eu.metaengine</groupId>
      <artifactId>metaengine-openapi-kotlin-ktor-maven-plugin</artifactId>
      <version>1.0.0</version>
      <executions>
        <execution>
          <id>generate-api-client</id>
          <goals>
            <goal>generate</goal>
          </goals>
          <configuration>
            <inputSpec>${project.basedir}/src/main/resources/openapi.yaml</inputSpec>
            <packageName>com.example.api</packageName>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

The generate goal binds to the generate-sources phase, so a normal mvn compile (or package) produces the client and registers it as a compile source root — no extra wiring. <inputSpec> accepts a local path or an http(s) URL; OpenAPI 3.0 and 3.1 in JSON or YAML.

Configuration examples
Auth, retries and a request timeout
<configuration>
  <inputSpec>${project.basedir}/openapi.yaml</inputSpec>
  <packageName>com.example.api</packageName>
  <bearerAuth>API_TOKEN</bearerAuth>
  <timeout>30</timeout>
  <retry>3</retry>
  <errorHandling>true</errorHandling>
</configuration>
Tag filtering, KDoc and Bean Validation
<configuration>
  <inputSpec>https://api.example.com/openapi.json</inputSpec>
  <packageName>com.example.api</packageName>
  <includeTags>payments,refunds</includeTags>
  <documentation>true</documentation>
  <validationAnnotations>true</validationAnnotations>
</configuration>
Basic auth and a tenant header from env vars
<configuration>
  <inputSpec>${project.basedir}/openapi.yaml</inputSpec>
  <packageName>com.example.api</packageName>
  <basicAuth>API_USER,API_PASS</basicAuth>
  <customHeaders>
    <customHeader>X-Tenant-ID:TENANT_ID</customHeader>
  </customHeaders>
</configuration>
Configuration
Element
Description
<inputSpec>
OpenAPI spec — local file path or http(s) URL. Required.
<packageName>
Kotlin package for the generated code, e.g. com.example.api (lowercase, dot-separated). Required.
<outputDirectory>
Output directory [default: ${project.build.directory}/generated-sources/metaengine]. Added to the compile source roots.
<includeTags>
Only generate operations carrying these tags (comma-separated)
<documentation>
Generate KDoc comments [default: false]
<bearerAuth>
Env var holding the bearer token — adds an Authorization header to every request
<bearerAuthHeader>
Custom header name for the bearer token [default: Authorization]. Requires bearerAuth.
<basicAuth>
HTTP Basic auth from env vars, format USER_VAR,PASS_VAR
<customHeaders>
Static headers from env vars (Header-Name:ENV_VAR) as <customHeader> children. Repeatable.
<timeout>
Request timeout in seconds (connect + read + write)
<timeoutConnect>
Connect timeout in seconds
<timeoutRead>
Read timeout in seconds (request timeout)
<timeoutWrite>
Write timeout in seconds (socket I/O)
<retry>
Enable retries with exponential backoff [default attempts: 3]
<errorHandling>
Smart error handling on the Ktor client — status-aware null / error / throw [default: false]
<validationAnnotations>
Emit Jakarta Bean Validation annotations on generated data classes [default: false]
<strictEnums>
Keep string enums strict — no synthetic UNKNOWN fallback for unknown wire values [default: false]
<optionsThreshold>
Parameter count threshold for the options-object pattern [default: 4]
<strictValidation>
Enable strict OpenAPI validation [default: false]
<clean>
Clean the output directory before generation (remove files not in generation) [default: false]
<verbose>
Enable verbose logging [default: false]
Options reference

Every knob, documented

Every option is available on the C# fluent API as a method; the Maven plugin exposes most as <configuration> elements. Cross-cutting auth, headers, retries and timeouts apply across frameworks.

Ktor Options

2
  • WithValidationAnnotations()Emit Jakarta Bean Validation annotations (@field:NotNull, @field:Size, @field:Pattern, @field:Min / @field:Max, @field:Email) on generated data classes from schema constraints (required, minLength/maxLength, minimum/maximum, format: email, pattern)
  • WithStrictEnums()Keep string enums strict (plain @Serializable — kotlinx throws on unknown values). By default a synthetic UNKNOWN member + companion KSerializer absorbs unrecognized wire values instead of crashing deserialization

Auth · Headers · Resilience

13
  • WithErrorHandling()Smart error handling on the Ktor client based on HTTP status (404 / 403 → null · 400 / 422 → error body · 401 / 5xx → throw)
  • WithErrorHandling(errors => errors...)Per-status routing: ReturnNullFor(404, 403) · ReturnErrorFor(400, 422) · ThrowFor(401, 500)
  • WithBearerAuth()Bearer token from env var (default API_TOKEN) — adds Authorization header
  • WithBearerAuth(string)Bearer token from a specific env var name
  • WithBearerAuth(string, string)Bearer token with custom header name (e.g. X-Api-Key)
  • WithBasicAuth()HTTP Basic auth from default env vars (API_USERNAME / API_PASSWORD)
  • WithBasicAuth(string, string)HTTP Basic auth from username + password env vars
  • WithCustomHeader(string, string)Static header from env var. Repeatable (e.g. X-Tenant-ID ← TENANT_ID)
  • WithTimeout(double)Request timeout in seconds — sets Ktor connect, request and socket timeouts together
  • WithTimeout(double?, double?, double?)Granular timeout: connect · read · write (Ktor HttpTimeout has no connection-pool timeout)
  • WithRetries()Retries with exponential backoff (default status 429, 503)
  • WithRetries(int)Retries with custom max attempts
  • WithRetries(int, double, double, int[])Retries with full custom settings including status codes

Kotlin Options

3
  • WithDocumentation()Generate KDoc comments on methods and parameters from the spec
  • WithMethodNames(Func)Custom method naming rule
  • WithOptionsObjectThreshold(int)Parameter count for options object (default: 4)

OpenAPI Filtering

3
  • WithStrictValidation()Enable strict OpenAPI validation
  • WithOperationFilter(Func)Filter operations by predicate
  • WithHeaderFilter(Func)Filter header parameters

Naming Transformations

3
  • Types(Func)Transform type names
  • Paths(Func)Transform output paths
  • FileNames(Func)Transform file names

File Management

5
  • CleanDestination()Clean output directory before generation
  • AlwaysOverwrite()Always overwrite existing files
  • OnlyWhenModelChanged()Update only when model changes
  • OnlyWhenNew()Write only new files, preserve existing
  • CleanDirectories(...)Clean specific subdirectories

Diagnostics

2
  • Verbose()Enable verbose logging
  • WithLogger(Action<string>)Route generation log output to a custom sink
Features

Why this package is different

Idiomatic data classes
kotlinx.serialization @Serializable data classes, nullable fields as T? = null, enums as @Serializable enum class.
Coroutine-first
Ktor client calls are suspend functions; streaming and subscriptions surface as Flow<T>.
Sealed hierarchies
Polymorphic payloads become sealed classes — exhaustive when with no else branch.
Deterministic output
Same spec + same options produce byte-identical files. Safe to commit, safe to diff in review, safe to cache in CI.
Flexible naming
Case conventions are configurable per role — types, properties, operations, enums. Idiomatic in the target by default.
Semver-honest
Spec diff drives the version bump. Additive changes = minor, removed operations = major. Never a surprise in your lockfile.