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
Convert OpenAPI to Kotlin Ktor in the browser
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.