OASmith generates focused Go, TypeScript, and Rust code from OpenAPI YAML or JSON documents. It supports focused generation modes without the runtime and configuration surface of a general-purpose OpenAPI generator.
| Mode | Language | Output |
|---|---|---|
types |
go |
Go models |
client |
go |
Go models and HTTP client |
client |
typescript |
TypeScript types and HTTP client |
types |
rust |
Serde models in mod.rs |
client |
rust |
Serde models and a Reqwest client in mod.rs |
OASmith handles the OpenAPI schema and operation subset covered by its fixture
suite, including objects, arrays, enums, oneOf discriminators, parameters,
request bodies, responses, and server-sent event operations.
OASmith requires Go 1.26 or newer.
go install github.com/responsibleapi/oasmith/cmd/oasmith@latestoasmith \
--openapi ./openapi.yaml \
--mode client \
--lang go \
--out ./gen/clientEvery invocation requires:
--openapi: input OpenAPI YAML or JSON document;--mode:typesorclient;--lang:go,typescript, orrust, subject to the supported pairs above;--out: generated output directory.
JSON input is supported alongside YAML. The document syntax is accepted
directly, so .json and .yaml file names work with the same command.
TypeScript output is written directly from the embedded templates without external tools.
Generated clients require an explicit client base URL and use it for every operation. OpenAPI server declarations do not change the runtime destination.
TypeScript clients emit JSON bodies, raw bodies as BodyInit, and fixed-length
ordered multipart bodies declared with prefixItems and prefixEncoding.
Binary multipart parts are Blob values; their media types must match the
content types declared by the corresponding prefix encoding. Unsupported
request-body shapes fail generation.
Generated clients leave OpenTelemetry dependencies and SDK setup to the
application. Pass an instrumented transport to a Go client or an instrumented
fetch implementation to a TypeScript client. Rust clients accept a
middleware-enabled HTTP client at construction.
These examples assume the application has initialized an OpenTelemetry SDK;
@opentelemetry/api alone uses no-op tracing and propagation implementations.
Install go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp, wrap an
explicit HTTP transport, and pass the active context.Context to every
generated operation:
transport := otelhttp.NewTransport(http.DefaultTransport)
httpClient := &http.Client{Transport: transport}
client, err := apiclient.NewClient(
apiclient.ClientOptions{BaseURL: "https://api.example.com"},
apiclient.WithHTTPClient(httpClient),
)
if err != nil {
return err
}
ctx, span := otel.Tracer("example-app").Start(ctx, "create thing")
defer span.End()
_, err = client.CreateThing(ctx, params)
return errThe generated request keeps the operation context. otelhttp.Transport reads
its span context and injects the configured propagation headers, such as
traceparent, before sending the request. Pass the derived ctx; replacing it
with context.Background() breaks the parent trace.
The generated ClientOptions.fetch hook can ask the registered global
propagator to inject the active OpenTelemetry context immediately before
transport execution:
import { context, propagation, trace } from '@opentelemetry/api';
import { DefaultApi } from './gen/api.ts';
const baseFetch = globalThis.fetch;
const otelFetch: typeof globalThis.fetch = async (input, init) => {
const request = new Request(input, init);
const headers = new Headers(request.headers);
propagation.inject(context.active(), headers, {
set(carrier, key, value): void {
carrier.set(key, value);
},
});
return await baseFetch(new Request(request, { headers }));
};
const api = new DefaultApi({
baseURL: 'https://api.example.com',
fetch: otelFetch,
});
const tracer = trace.getTracer('example-app');
await tracer.startActiveSpan('create thing', async span => {
try {
await api.createThing(params);
} finally {
span.end();
}
});The application SDK must register both a context manager and a text-map
propagator. context.active() must contain a valid span context, and a W3C Trace
Context propagator must be registered for propagation.inject to add
traceparent; otherwise it adds no trace header. For browser calls across
origins, the API's CORS policy must also allow the propagation headers configured
by the application, commonly traceparent, tracestate, and baggage.
The insertion point is the HTTP client passed to api::Client::new. Configure
reqwest-tracing
middleware once; every generated operation's .send() then propagates the active
trace automatically:
let http = reqwest_middleware::ClientBuilder::new(reqwest::Client::builder().build()?)
.with(reqwest_tracing::TracingMiddleware::default())
.build();
let api = api::Client::new(http, "https://api.example.com".into(), None);
// Inside the application's existing tracing span:
let response = api.create_thing(params).send().await?;
let result = api::CreateThingResponse::decode(response).await?;For the generated Reqwest 0.12 client, use reqwest-middleware 0.4.2 with its
json feature and reqwest-tracing 0.5.8 with its opentelemetry_0_30 feature.
The application must already have OpenTelemetry 0.30, a tracing-opentelemetry
0.31 subscriber layer, and a registered W3C TraceContextPropagator.
The middleware reads the current tracing span when the request is sent, creates
a child HTTP span, and injects its traceparent and tracestate headers.
One shared client can therefore serve calls from different traces. Normal async
context propagation still applies: instrument spawned tasks with
.in_current_span().
Go builds the generator and runs its tests. Moon runs the complete project check.
moon run checkCI follows Moon's CI guide: full Git
history, source-based affected selection, and plain moon ci. It runs the same
project tasks used locally; aggregate checks and maintenance commands are excluded
from automatic selection. The same moon.yml also works as a Listenbox submodule.
Moon sets GOCACHE to ~/.cache/go-build on macOS and Linux, shared across
worktrees. Modules use Go's shared module cache (go env GOMODCACHE). Moon uses
~/.cache/golangci-lint for linter data; temporary files use the system temp
directory. The same cache locations apply in standalone and parent workspaces.
The lint task allows parallel runners without the global linter lock.
Moon task results
are not restored across CI runs. Sources, module files, fixtures, templates, and
configuration determine affected tasks; $CI is not an input. CI verifies
formatting, and selected Go tests bypass Go's test-result cache. Native Moon
reports are attached to the workflow, including on failure.
Use --mode client --lang rust --out src/api, then mod api;. Add serde 1
(with derive), serde_json 1, reqwest 0.12, and reqwest-middleware 0.4.2
(with json) to Cargo dependencies.
Choose the Reqwest TLS features appropriate to your application.
Construct api::Client::new(http, base_url, bearer_token) with your configured
reqwest_middleware::ClientWithMiddleware. For a client without middleware,
construct it with reqwest_middleware::ClientBuilder::new(http).build().
Operation methods return reqwest_middleware::RequestBuilder, so callers own timeouts,
cancellation, tracing, and bounded body reads. Models and operation-specific
Response::decode enums preserve declared HTTP statuses; undeclared statuses
retain their original response. SSE responses remain streaming Reqwest responses,
leaving event framing and cancellation to the caller.
See Rust trace propagation to configure automatic propagation once.
Rust supports JSON and raw request bodies, optional bodies, scalar and repeated
query parameters, headers, escaped path parameters, enums, nullable values and
untagged oneOf models. Sequential multipart requests currently fail generation
with an explicit unsupported-body error. Run moon run oasmith:test-rust to generate,
compile, and execute the Rust contract fixtures.