arvalez
Modern OpenAPI client generator with a compiler-style core and typed IR.
Current Scope
This first implementation slice focuses on the OpenAPI importer and the target backends:
- shared IR types in Rust
- a first Python backend that emits Pydantic models plus sync and async
httpxclients - a first TypeScript backend that emits typed models plus a
fetch-based client - a first Go backend that emits typed models plus a
net/httpclient
The OpenAPI importer is in place, and the generator can emit Python, TypeScript, and Go packages from openapi.json.
Workspace Layout
crates/arvalez-ir: shared IR and validationcrates/arvalez-openapi: OpenAPI document loader and Core IR mappercrates/arvalez-target-go: Go SDK generatorcrates/arvalez-target-python: Python SDK generatorcrates/arvalez-target-typescript: TypeScript SDK generatorcrates/arvalez-cli: local CLI for inspecting IR and generating SDKs
Build
Build Core IR from the local OpenAPI document:
Add --timings to print a per-phase breakdown for import and generation work:
Generate a Python SDK package:
Generate a TypeScript SDK package:
Generate a Go SDK package:
The generators also read optional settings from arvalez.toml:
[]
= "generated"
= true
= "1.0.0"
[]
= "github.com/acme/client"
= "client"
= "1.0.0"
[]
= "arvalez_client"
= "1.0.1"
= "./templates/python"
[]
= "@arvalez/client"
= "1.0.2"
= "./templates/typescript"
Override the configured output version from the CLI:
Generate all enabled backends into one output root:
Run the APIs.guru corpus as an on-demand generation test:
By default this command clones APIs-guru/openapi-directory, discovers every openapi.json, openapi.yaml, swagger.json, and swagger.yaml, and runs generation for each spec. Generated outputs go to a temporary directory unless you pass --output-directory, so the repository stays slim.
The checkout is cached under .arvalez/corpus/openapi-directory/ in the current workspace and refreshed on later runs, so you do not pay the full clone cost every time.
Reports are written into the chosen directory only as timestamped JSON files like apis-guru-1774593522.json.
The report dashboard is now a SvelteKit app in web/corpus-dashboard. It serves the report directory through a small local server, watches for new report JSON files, and updates the UI in real time.
For local dashboard development, run npm install once in web/corpus-dashboard/, then:
REPORT_DIRECTORY=/absolute/path/to/reports/apis-guru
If REPORT_DIRECTORY is not set, the app defaults to ../../reports/apis-guru relative to web/corpus-dashboard/.
Use --jobs N to control spec-level parallelism; by default Arvalez uses the machine's available parallelism.
Pass --ui on a local terminal to get a live ratatui dashboard with progress, active specs, and recent completions. Press q to hide the UI and fall back to plain progress lines while the run continues.
Disable a backend from the CLI:
Disable a backend from config:
[]
= true
= "@arvalez/client"
The Go backend also supports bundled default Tera templates with selective overrides:
Supported override names are:
package/go.mod.terapackage/README.md.terapackage/models.go.terapackage/client.go.terapartials/model_struct.go.terapartials/service.go.terapartials/client_method.go.tera
When group_by_tag = true, tagged operations are grouped under subclients. For example, Python becomes client.ingredients.create_ingredient(...) and TypeScript becomes client.ingredients.createIngredient(...). Operations without tags stay on the root client, and multi-tag operations use the first tag.
Shared settings in [output] act as defaults, and [output.python] / [output.typescript] / [output.go] can override them per target. That includes group_by_tag, version, and similar cross-target options. CLI flags like --output-version override both.
Override only selected Python templates:
The Python backend ships with bundled default Tera templates inside the binary. Override files are optional and can be provided selectively. Supported override names are:
package/pyproject.toml.terapackage/README.md.terapackage/__init__.py.terapackage/models.py.terapackage/client.py.terapartials/model_class.py.terapartials/client_class.py.terapartials/client_method.py.tera
Override only selected TypeScript templates:
The TypeScript backend also ships with bundled default Tera templates inside the binary. Supported override names are:
package/package.json.terapackage/tsconfig.json.terapackage/README.md.terapackage/models.ts.terapackage/client.ts.terapackage/index.ts.terapartials/model_interface.ts.terapartials/client_method.ts.terapartials/tag_group.ts.tera
Inspect the raw fixture IR:
Docker
Build a container image with the precompiled CLI:
Run the bundled tool against files mounted from the current workspace:
Generate a Python SDK from inside the container:
Releases
Publishing a GitHub release triggers .github/workflows/release.yml, which:
- checks that the release tag matches the workspace version in
Cargo.toml - builds and pushes the multi-arch
arvalez/cliimage to Docker Hub - publishes the Rust crates to crates.io in dependency order
The workflow expects these GitHub repository secrets:
DOCKERHUB_USERNAMEDOCKERHUB_TOKENCARGO_REGISTRY_TOKEN
Set up pre-commit once after cloning:
The pre-commit config keeps internal workspace dependency versions aligned with the workspace version and regenerates Cargo.lock whenever Cargo manifests are part of the commit.