Native SDK codegen — @ir-kit/openapi-{go,kotlin,swift,typescript}
One programmatic interface across four target languages. All four expose generate({ input, output }) with the same input-handling: file path, URL, or pre-parsed object. The three native generators (Go / Kotlin / Swift) default to normalize: true and route the spec through a shared safe-normalize preset so OpenAPI 2.0 / 3.0 / 3.1 all produce consistent output. The TypeScript generator is the exception — it forwards to @hey-api/openapi-ts which owns its own normalization, so normalize defaults to false there (pass normalize: true to opt into the same pre-pass before hey-api's pipeline).
Picking the right package
| Language | Package | Output style |
|---|---|---|
| Go | @ir-kit/openapi-go |
net/http + encoding/json + context.Context. Optional go.mod. |
| Kotlin | @ir-kit/openapi-kotlin |
OkHttp + kotlinx-serialization + suspend functions. Optional Gradle wrapper. |
| Swift | @ir-kit/openapi-swift |
Codable + URLSession + async throws. Optional Package.swift. |
| TypeScript | @ir-kit/openapi-typescript |
Thin wrapper over @hey-api/openapi-ts (default plugins: client-fetch + typescript + sdk). Plug in any hey-api plugin. |
If the user wants multiple languages at once, call generate on each in parallel — they're independent.
Universal shape (Go / Kotlin / Swift)
import { generate } from "@ir-kit/openapi-go"; // or -kotlin, -swift
await generate({
input: "./openapi.yaml", // path, URL, or pre-parsed object
output: "./sdk/go", // directory; created if missing
clean: true, // wipe output before writing (default)
normalize: true, // apply safe-normalize preset (default)
// ... language-specific options (see references/per-language.md)
});
The result is { files: BuiltFile[], output: string } — file list relative to the output dir.
TypeScript variant
Wraps @hey-api/openapi-ts's createClient so the same workflow targets TS clients via hey-api's full plugin ecosystem (Faker, TanStack Query, oRPC, validators, …):
import { generate } from "@ir-kit/openapi-typescript";
await generate({
input: "./openapi.yaml",
output: "./sdk/ts",
// Default: ["@hey-api/client-fetch", "@hey-api/typescript", "@hey-api/sdk"]
plugins: [
"@hey-api/client-fetch",
"@hey-api/typescript",
"@hey-api/sdk",
"@tanstack/react-query", // hey-api plugin
"@ir-kit/openapi-ts-faker", // ir-kit plugin
],
heyApi: { // pass-through for any hey-api UserConfig field
parser: { transforms: { enums: "root" } },
},
});
Spec input — all three shapes work everywhere
@ir-kit/openapi-tools ships a loadSpec() that every generator calls. Inputs accepted:
- File path —
./openapi.yaml,./openapi.json, absolute or relative (resolved againstcwdif you pass that option). - URL —
https://api.example.com/openapi.yamlorhttp://localhost:8080/spec.json. - Pre-parsed object — already-loaded JS object (skips fetch + JSON.parse).
The loader auto-detects scheme and routes through @hey-api/json-schema-ref-parser for bundling external $refs inline.
Multi-language driver pattern
A script that generates all four SDKs into one output tree:
import { generate as go } from "@ir-kit/openapi-go";
import { generate as kotlin } from "@ir-kit/openapi-kotlin";
import { generate as swift } from "@ir-kit/openapi-swift";
import { generate as ts } from "@ir-kit/openapi-typescript";
const input = "./openapi.yaml";
await Promise.all([
go({ input, output: "./sdks/go", gomod: { module: "github.com/example/petstore-sdk" } }),
kotlin({ input, output: "./sdks/kotlin", gradle: { artifact: "com.example.petstore" } }),
swift({ input, output: "./sdks/swift", package: { name: "PetstoreSDK" } }),
ts({ input, output: "./sdks/ts" }),
]);
This is what examples/openapi-sdk-petstore/ in ir-kit does.
Language-specific bundling
Each native generator has an optional flag for emitting a package manifest:
- Go —
gomod: { module: "..." }writesgo.modat the output root. - Kotlin —
gradle: { artifact: "..." }writesbuild.gradle.kts+settings.gradle.kts. - Swift —
package: true(sensible defaults) or{ name, platforms, ... }writesPackage.swift. - TypeScript — output is hey-api's existing layout; no extra manifest.
Omit the flag for a "flat source tree" output the user can drop into an existing module/project.
See references/per-language.md for the full per-language option surface and idiomatic-output examples.
Common pitfalls
outputis wiped by default (clean: true). Don't point it at a directory containing hand-written files. Passclean: falseto merge.- Spec 2.0 / 3.0 / 3.1 all work —
normalize: true(the default) routes through hey-api's IR so the output shape is identical across input versions. gomod/gradle/packageare optional. Without them, the generator emits only source files — ready forgo mod init-style consumption in an existing module.- TS generator uses hey-api's plugin order matters —
client-fetchmust come beforesdk. - Authentication is consumer's responsibility. None of these generators emit auth-aware constructors by default — they emit clients you wire interceptors/headers onto. (TS hey-api plugins like
client-fetchexpose middleware hooks for this.)
How AI agents should use this
- Ask the user which language(s) they want — one or many.
- For 1 language: show the matching
generate({ input, output, … })snippet from this skill. - For multiple languages: show the multi-language driver pattern above.
- If the user wants language-specific tuning (Go module path, Kotlin Gradle artifact, Swift platforms, TS hey-api plugins) → load
references/per-language.md. - For TypeScript specifically, mention that the user can plug in hey-api's wider ecosystem (TanStack Query, oRPC, validators, Faker) via the
pluginsarray.
Source: ir-kit/ir-kit — distributed by TomeVault.