Code Generation¶
APX generates language-specific code from schema files, placing the output in overlay directories with canonical import paths. This page covers the generation process, supported languages, and output structure.
Overview¶
The apx gen command reads your schemas from internal/apis/, generates code using format-specific toolchains (e.g. Buf for protobuf), and writes the output to internal/gen/<lang>/ with canonical module paths.
Supported languages: go, python, java, typescript
How It Works¶
- Load dependencies — reads
apx.lockfor pinned tool and dependency versions - Create overlays — for each dependency, creates a directory in
internal/gen/<lang>/with the canonical module structure - Generate code — runs format-specific code generators (e.g.
buf generatefor protobuf) - Synthesize go.mod — for Go, creates a
go.modwith the canonical module path sogo.workcan resolve imports - Sync go.work — for Go, automatically adds
usedirectives for each overlay
Go Generation¶
Output Structure¶
internal/gen/go/
└── proto/payments/ledger@v1.2.3/
├── go.mod # module github.com/<org>/apis/proto/payments/ledger
└── v1/
├── ledger.pb.go # generated protobuf code
└── ledger_grpc.pb.go # generated gRPC stubs
The @v1.2.3 suffix is the pinned version from apx.lock. The go.mod declares the canonical module path, enabling go.work overlay resolution.
go.work Integration¶
After generating Go code, apx gen go automatically calls apx sync to update go.work:
go 1.22
use .
use ./internal/gen/go/proto/payments/ledger@v1.2.3
use ./internal/gen/go/proto/users/profile@v1.0.1
Your application code then imports canonical paths:
Go resolves this to the local overlay via go.work during development.
Python Generation¶
When org is configured in apx.yaml, apx gen python scaffolds each overlay as an installable Python package with:
pyproject.toml— PEP 621 metadata with deterministic dist name (<org>-<domain>-<api>-<line>)__init__.pyhierarchy — namespace packages usingpkgutil.extend_path
Output Structure¶
internal/gen/python/
└── proto/payments/ledger/v1/
├── pyproject.toml # name = "acme-payments-ledger-v1"
└── acme_apis/
├── __init__.py # pkgutil.extend_path (namespace root)
└── payments/
├── __init__.py
└── ledger/
├── __init__.py
└── v1/
└── __init__.py # leaf — generated code lands here
Editable Install¶
Link Python overlays into your virtualenv for import resolution:
# Activate your virtualenv
source .venv/bin/activate
# Link all Python overlays (runs pip install -e for each)
apx link python
# Then import in your code:
# from acme_apis.payments.ledger.v1 import ledger_pb2
This mirrors Go's go.work overlay approach — code is generated locally, not pulled from a registry. You control the grpc/protobuf versions in your own virtualenv.
Switching to Released Package¶
Your import paths stay the same — from acme_apis.payments.ledger.v1 import ...
Java Generation¶
Java takes a different approach from Go and Python: schemas are the published artifact. Rather than generating and publishing Java stubs, APX publishes schema files (proto zip/jar) to a Maven repository. Consumers generate Java code locally via Maven's generate-sources phase.
Maven Coordinate Derivation¶
APX deterministically derives Maven coordinates from the API identity:
| Component | Pattern | Example |
|---|---|---|
| groupId | com.<org>.apis | com.acme.apis |
| artifactId | <domain>-<name>-<line>-proto | payments-ledger-v1-proto |
| Java package | com.<org>.apis.<domain>.<name>.<line> | com.acme.apis.payments.ledger.v1 |
The -proto suffix on the artifactId distinguishes schema artifacts from hypothetical code artifacts.
Consumer Workflow¶
Add the schema artifact to your pom.xml:
<dependency>
<groupId>com.acme.apis</groupId>
<artifactId>payments-ledger-v1-proto</artifactId>
<version>1.2.3</version>
</dependency>
Configure protobuf-maven-plugin to generate Java from the schema artifact during generate-sources:
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
</configuration>
<executions>
<execution>
<goals><goal>compile</goal><goal>compile-custom</goal></goals>
</execution>
</executions>
</plugin>
Generated Java code lands in target/generated-sources/protobuf/ and is compiled as part of the normal Maven build.
Local Development¶
For local development before schemas are released, use apx link java (planned) to install schema artifacts to ~/.m2/repository:
# Generate and install to local Maven cache
apx link java # planned — installs to ~/.m2
# Maven resolves from local cache
mvn compile
This mirrors Go's go.work overlay and Python's pip install -e — same identity in dev and prod, only the resolution backend changes.
TypeScript Generation¶
TypeScript follows the same schema-first pattern. APX derives scoped npm package names from the API identity:
| Component | Pattern | Example |
|---|---|---|
| npm package | @<org>/<domain>-<name>-<line>-proto | @acme/payments-ledger-v1-proto |
The -proto suffix distinguishes schema packages from application packages.
Consumer Workflow¶
Install the schema package from npm:
Import generated types in your TypeScript code:
Local Development¶
For local development before schemas are released, use apx link typescript (planned) to create npm links:
# Generate and link locally
apx link typescript # planned — creates npm link
# npm resolves from local link
npm run build
This mirrors Go's go.work, Python's pip install -e, and Java's ~/.m2 — same identity in dev and prod.
Flags¶
| Flag | Type | Default | Description |
|---|---|---|---|
--out | string | "" | Override the output directory |
--clean | bool | false | Remove existing output before generating |
--manifest | bool | false | Emit a generation manifest listing all produced files |
Clean Generation¶
Use --clean when switching versions or after changing dependencies to avoid stale files:
This removes all existing overlays in internal/gen/go/ before regenerating.
Format-Specific Toolchains¶
Code generation uses format-specific tools, resolved from apx.lock:
| Format | Tool | Plugins |
|---|---|---|
| Protocol Buffers | buf | protoc-gen-go, protoc-gen-go-grpc |
| OpenAPI | — | Language-specific client generators |
| Avro | avro-tools | Language-specific serializers |
| JSON Schema | — | Schema-to-code generators |
| Parquet | — | Schema readers |
Toolchain versions are pinned in apx.lock and downloaded with apx fetch.
.gitignore Policy¶
Generated code must never be committed. Add to .gitignore:
CI regenerates code from apx.lock during each pipeline run, ensuring consistency.
Workflow Integration¶
Go Development Loop¶
apx add <api-id>— add dependencyapx gen go— generate Go code with canonical importsapx sync— updatego.workto include overlays- Edit and test locally — Go toolchain resolves via
go.work apx unlink <api-id>— switch to released module
C++ Development Loop¶
- Add
{org}-{domain}-{name}-{line}-prototo yourconanfile conan install .— resolve dependencies- Include
{org}/{domain}/{name}/{line}headers in C++ code - Local development via
conan editable addfor overlay resolution
Java Development Loop¶
- Add
<dependency>topom.xmlwith derived Maven coordinates mvn generate-sources— generate Java code from schema- Import
com.{org}.apis.{domain}.{name}.{line}.*in Java code - Local development via
mvn installto~/.m2repository
Python Development Loop¶
apx add <api-id>— add dependencyapx gen python— generate Python code with namespace packagesapx link python— runpip install -ein active virtualenvfrom {org}_apis.{domain}.{name}.{line} import ...— import generated codeapx unlink <api-id>— switch to released package viapip install
Rust Development Loop¶
- Add
{org}-{domain}-{name}-{line}-prototo[dependencies]inCargo.toml cargo build— compile with generated codeuse {org}_{domain}::{name}::{line}::*in Rust code- Local development via path dependency for overlay resolution
TypeScript Development Loop¶
npm install @{org}/{domain}-{name}-{line}-proto— add dependencyimport { ... } from '@{org}/{domain}-{name}-{line}-proto'— import in code- Local dev via
npm linkfor development iteration apx unlink <api-id>— switch back to released npm package
CI Pipeline¶
apx fetch # download pinned tools
apx gen go # regenerate from lock file
apx sync # update go.work
go test ./... # canonical imports resolve via overlay
Language-Specific Generation Reference¶
Go¶
APX manages Go overlays through go.work files, providing seamless local development without replace directives.
This creates local overlays in .apx/overlays/go/ and updates go.work to include them. Generated code uses canonical import paths so it works identically in local development and after release.
Key characteristics: - Module path follows Go major version conventions (no suffix for v0/v1, /vN for v2+) - Import path always includes the line version (/v1, /v2, etc.) - go.work overlays enable local development without replace directives - apx sync keeps go.work in sync after adding/removing dependencies
Python¶
APX scaffolds Python namespace packages with PEP 625 distribution names and pkgutil-based namespace packages.
This creates overlays in .apx/overlays/python/ with: - pyproject.toml — distribution metadata with the derived dist name - __init__.py hierarchy using pkgutil.extend_path for namespace packages - Generated protobuf/schema code
Key characteristics: - Distribution name: {org}-{domain}-{name}-{line} (e.g. acme-payments-ledger-v1) - Import path: {org}_apis.{domain}.{name}.{line} (e.g. acme_apis.payments.ledger.v1) - Editable install via apx link python (runs pip install -e) - Requires org in apx.yaml for package naming
Java¶
Java APIs are published as Maven artifacts using conventional groupId and artifactId derivation from the API identity.
Maven coordinates are derived as: - groupId: com.{org}.apis (hyphens become dots) - artifactId: {domain}-{name}-{line}-proto
Key characteristics: - Full Maven coordinate: com.{org}.apis:{domain}-{name}-{line}-proto - Java package: com.{org}.apis.{domain}.{name}.{line} - Consumer adds <dependency> to pom.xml with the derived coordinates - Local dev via mvn install to local ~/.m2 repository - Requires org in apx.yaml for Maven coordinate derivation
C++¶
C++ APIs are published as Conan packages using conventional naming derived from the API identity.
Conan package references are derived as: - Package name: {org}-{domain}-{name}-{line}-proto - C++ namespace: {org}::{domain}::{name}::{line}
Key characteristics: - Conan reference: {org}-{domain}-{name}-{line}-proto - C++ namespace: {org}::{domain}::{name}::{line} - Consumer adds dependency to conanfile.txt or conanfile.py - Local dev via conan editable add for development iteration - Requires org in apx.yaml for Conan reference derivation
Rust¶
Rust APIs are published as Cargo crates using conventional naming derived from the API identity.
Crate names are derived as: - Crate: {org}-{domain}-{name}-{line}-proto - Rust module: {org}_{domain}::{name}::{line}
Key characteristics: - Crate name: {org}-{domain}-{name}-{line}-proto - Rust module path: {org}_{domain}::{name}::{line} - Consumer adds [dependencies] entry to Cargo.toml - Local dev via path dependencies for development iteration - Requires org in apx.yaml for crate name derivation
TypeScript¶
TypeScript APIs are published as scoped npm packages with a -proto suffix.
Key characteristics: - npm package: @{org}/{domain}-{name}-{line}-proto - Import path equals the npm package name - Consumer installs via npm install or yarn add - Local dev via npm link for development iteration - Requires org in apx.yaml for package scoping
See Also¶
- Local Development — full development workflow
- Adding Dependencies — pin schemas before generating
- App Repo Layout — where generated code lives