The generator collection
This directory holds a collection of openapi-yagen generators. Each one is meant to be a
self-contained subproject: easy to copy out into its own git repository, and just as easy to
drop a standalone generator from somewhere else into this collection.
Available generators
Built into the openapi-yagen binary itself (usable via -g builtin:<name> with no local
checkout, zip, or network access - see the root README.md):
builtin:kotlin_ktor_clientbuiltin:kotlin_ktor_serverbuiltin:typescript_fetch_clientbuiltin:ruby_faraday_clientbuiltin:ruby_rails_serverbuiltin:python_tornado_serverbuiltin:go_net_http_clientbuiltin:go_net_http_server
Run openapi-yagen list-generators for this same list straight from whatever binary you have
installed. The full collection (every generator above is also listed here, plus
sample_cpp_models_generator, which isn't built in):
sample_cpp_models_generator- minimal example generating C++ model structs from schemas.kotlin_ktor_client_generator- Kotlin Multiplatform API client for Ktor, engine-agnostic (works on JVM, Android, iOS/Native, JS, Wasm).kotlin_ktor_server_generator- Ktor server routing + a handler interface you implement, with request validation.typescript_fetch_client_generator- browser-first TypeScript API client using nativefetch, zero third-party runtime dependencies, works unchanged from any web framework.ruby_faraday_client_generator- Ruby API client built on Faraday, taking a caller-suppliedFaraday::Connection, adapter-agnostic (works withnet_http, Typhoeus, httpx, ...).python_tornado_server_generator- Tornado server routing + a handler interface you implement, with request validation (Python has no compiler to catch a wrong-shaped value, so every model gets a generatedvalidate()).go_net_http_client_generator- Go API client on the standard library'snet/http/encoding/jsononly, taking a caller-supplied*http.Client, zero third-party dependencies.go_net_http_server_generator- Go HTTP handlers on the standard library'snet/http(Go 1.22+ServeMuxrouting), with request validation, zero third-party dependencies.ruby_rails_server_generator- Ruby on Rails controllers + routing, with request validation - you implement a plain Ruby class per tag against a generated handler interface, noActionControllersubclassing needed.
Why generators can be self-contained
openapi-yagen's -g flag accepts a directory, a zip archive, or an HTTP(S) URL, and resolves
every file the generator needs - generator.yml, main.js, templates, copyFile sources,
even the ES-module imports inside main.js - strictly relative to whatever root you pointed -g
at. Nothing in the engine assumes a generator lives under this repo's generators/ directory, or
knows this directory exists at all. That means a generator directory carries everything it needs
to generate code, and anything else placed alongside it (like a test project) is simply invisible
to the generation engine unless a template or script explicitly reads it.
The README.md / src/ / test/ convention
Every generator in this collection follows the same shape:
generators/<name>/
README.md # usage docs for this generator: variables, output layout, known limitations
src/ # everything -g points at: generator.yml, main.js, lib/, templates/
test/ # (optional) this generator's own runtime test suite
src/is the actual generator payload.-g generators/<name>/srcis how you invoke it.README.mddocuments the generator for someone using it - not for someone testing the engine itself.test/is optional and only present once a generator has a real test suite. If present, it's an independently runnable project (see below) - nothing else in this repo needs to know it exists for the generator itself to work.
Running one generator's tests in isolation
The Kotlin generators' test suites are Kotlin Multiplatform (jvm() + linuxX64()) Gradle
projects, each with its own Gradle wrapper committed - no separate Gradle install needed, just a
JDK (a first linuxX64 compile also downloads the Kotlin/Native toolchain itself, a one-time,
sizeable download):
cd generators/kotlin_ktor_client_generator/test
OPENAPI_YAGEN=/path/to/openapi-yagen ./gradlew test
Each regenerates its own code from a small "kitchen-sink" spec (test/resources/kitchensink.yaml)
via the openapi-yagen CLI, compiles it alongside hand-written tests, and actually runs the
generated code - server tests boot it in-memory via Ktor's testApplication{} against fake
handler implementations; client tests point it at ktor-client-mock's MockEngine - asserting
on every generated operation's positive and negative (validation, not-found, etc.) behavior. No
real network or socket is involved in either. ./gradlew test only exercises the JVM target (the
generated code lives in commonMain, but ktor-*-test-host/ktor-client-mock/JUnit5 are all
JVM-only, so the hand-written tests live in jvmTest) - ./gradlew compileKotlinLinuxX64 proves
the same generated code also compiles under Kotlin/Native (no test run there, just compile - see
each generator's README "Try it" / this repo's CI).
OPENAPI_YAGEN points it at a prebuilt openapi-yagen binary. Without it, the build falls back
to this checkout's own dist/openapi-yagen - make sure that's up to date (./build-musl.sh or a
local cmake --build) before relying on the fallback, or just set OPENAPI_YAGEN explicitly.
typescript_fetch_client_generator's test suite follows the same idea with a different toolchain
(npm/TypeScript instead of Gradle/JUnit5, since there's nothing Gradle-shaped to generate here):
cd generators/typescript_fetch_client_generator/test
OPENAPI_YAGEN=/path/to/openapi-yagen npm install && npm test
It regenerates from its own test/resources/kitchensink.yaml, typechecks the result with tsc
(once against the default extensionless import style, once against -v importExtension=.js), and
runs the .js-extension variant with Node's built-in node:test against a hand-rolled fetch
stub (test/src/support/fetchStub.ts) - no mocking library, same OPENAPI_YAGEN-env-var-with-
dist/openapi-yagen-fallback convention as the Gradle projects.
go_net_http_client_generator/go_net_http_server_generator's test suites use go generate
(a //go:generate ./generate.sh directive in test/generate.go) as the regenerate-before-compile
step, since a Go test binary can't regenerate its own statically-imported packages at run time the
way a JVM/Node test runner can shell out mid-run:
cd generators/go_net_http_client_generator/test
OPENAPI_YAGEN=/path/to/openapi-yagen go generate ./... && go test ./...
It regenerates from its own test/resources/kitchensink.yaml into test/generated/ (gitignored),
then runs ordinary go test against it - client tests point the generated client at an
httptest.Server with a hand-rolled handler; server tests mount the generated routes onto a fresh
http.ServeMux wrapped in httptest.NewServer and hit it with net/http - no mocking library,
same OPENAPI_YAGEN-env-var-with-dist/openapi-yagen-fallback convention as every other
generator's test project (built into generate.sh itself here, since Go has no build-tool-level
env-var substitution the way Gradle/npm scripts do it).
A generator's test/ project has exactly one relative reference back into this repo: ../src,
pointing at its own sibling generator directory. That's it - no root settings.gradle.kts/
package.json, no composite build, no shared build logic. This is deliberate: a test/ project
must stay runnable completely on its own, with zero awareness of being inside this particular
checkout.
Extracting a generator into its own repository
Because of the above, this is a plain directory copy:
cp -r generators/kotlin_ktor_client_generator /path/to/new-repo/
No paths need rewriting anywhere - -g doesn't care where the directory lives, and the test/
project's only repo-relative reference (../src) still resolves correctly since it's relative to
the generator's own directory, not to this repo's root.
Importing a standalone generator into this collection
Drop its directory under generators/. If it ships its own test/, ./scripts/test-all-generators.sh
(see below) will pick it up automatically - no registration or configuration needed anywhere else
in this repo.
Reference example
generators/kotlin_ktor_client_generator/ and generators/kotlin_ktor_server_generator/ are the
fullest worked example of this convention for a Gradle/JVM toolchain - read their test/
directories if you're adding a Gradle-based test suite to another generator.
generators/typescript_fetch_client_generator/ is the worked example for an npm/TypeScript
toolchain instead. generators/sample_cpp_models_generator/ follows the README.md/src/
convention but doesn't have a test/ yet (an open item, not specific to any one language or
toolchain - a future C++ generator test suite doesn't need Gradle or npm, or any particular tool
at all; it just needs to live in that generator's own test/ directory and
regenerate-then-verify on its own).
Running every generator's tests
./scripts/test-all-generators.sh
Globs generators/*/test/build.gradle.kts (running ./gradlew test in each),
generators/*/test/package.json (running npm install && npm test in each),
generators/*/test/Gemfile (running bundle install && bundle exec rake test in each),
generators/*/test/requirements.txt (running python3 -m venv .venv && .venv/bin/pip install -r requirements.txt && .venv/bin/pytest in each - see python_tornado_server_generator/test/ for the
worked example), and generators/*/test/go.mod (running go generate ./... && go test ./... in
each - see go_net_http_server_generator/test/ for the worked example). Pure convenience glue, not
a build dependency of anything else - new generators are auto-discovered by whichever glob matches
their own test/ project, no registration needed anywhere.