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#
apx gen <lang> [path]
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
How It Works#
Load dependencies β reads
apx.lockfor pinned tool and dependency versionsCreate overlays β for each dependency, creates a directory in
internal/gen/<lang>/with the canonical module structureGenerate 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 importsSync go.work β for Go, automatically adds
usedirectives for each overlay
Go Generation#
apx gen go
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:
import ledgerv1 "github.com/acme-corp/apis/proto/payments/ledger/v1"
Go resolves this to the local overlay via go.work during development.
Python Generation#
apx gen python
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#
apx unlink proto/payments/ledger/v1
pip install acme-payments-ledger-v1==1.2.3
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 |
|
|
artifactId |
|
|
Java package |
|
|
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.
Flags#
Flag |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Override the output directory |
|
bool |
|
Remove existing output before generating |
|
bool |
|
Emit a generation manifest listing all produced files |
Clean Generation#
Use --clean when switching versions or after changing dependencies to avoid stale files:
apx gen go --clean
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 |
|
|
OpenAPI |
β |
Language-specific client generators |
Avro |
|
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:
internal/gen/
CI regenerates code from apx.lock during each pipeline run, ensuring consistency.
Workflow Integration#
Local Development β Go#
apx gen go && apx sync
go test ./...
Local Development β Python#
apx gen python
source .venv/bin/activate
apx link python
pytest
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
See Also#
Local Development β full development workflow
Adding Dependencies β pin schemas before generating
App Repo Layout β where generated code lives