Warning
This repository is experimental. Generated APIs, input formats, and CLI behavior may change without compatibility guarantees.
Rust CLI for generating language-specific Nexus operation bindings from WIT.
The WIT definition is the source of truth for the public API. Protobuf descriptor sets are optional and are only needed when the WIT opts into proto-backed models or when using add-rpc to scaffold WIT from an existing proto RPC.
Current status:
- Python generation is implemented
- TypeScript generation is implemented
- WIT records, enums, flags, variants, results, resources, resource methods, and no-result operations are supported without proto backing
- proto-backed request models can serialize into proto inputs when WIT types are annotated with
@nexus.proto - proto-backed response and nested models remain bidirectional where generated
- support files, native type substitutions, sourced fields, function metadata, flattened API fields, and output transforms are driven from WIT
@nexusdirectives
Each example starts with authored WIT under examples/inputs/. Checked-in
generated output lives under examples/python/<example_name>/ and
examples/typescript/<example-name>/, with language-specific tests under each
language's tests/ directory. See examples/README.md
for links to each example's WIT, generated code, and tests.
user-service: a small WIT-direct API showing the basic shape of an operation returning a resource and a resource method that calls another operation.type-showcase: a WIT-direct API focused on type coverage, including records, enums, flags, variants, results, maps, tuples, resources, resource methods, and no-result operations.start-workflow: a proto-backed Temporal workflow-start API that returns a generated resource handle with follow-up operations such as cancel, restart, and get-result.workflow-service: a proto-backedSignalWithStartWorkflowExecutionexample showing flattened APIs, function arguments, sourced fields, support converters, and output transforms.type-roundtrip: a proto-backed type roundtrip example for focused native/proto conversion coverage, including retry policies, activity options, durations, task queues, and priority.
Rebuild the checked-in example outputs:
cargo build-examplesRebuild one language or one example only:
cargo build-examples --lang python
cargo build-examples user-service
cargo build-examples --lang typescript user-serviceRun the same validations as the CI pipeline:
./scripts/validate.shWrite the prepared WIT workspace the loader actually parses:
cargo run -- debug-wit-dir \
--input examples/inputs/user-service.wit \
--output /tmp/user-service-witStart with a WIT file that describes the API surface directly:
package temporal:user-service@1.0.0;
world system {
export user-service;
}
/// @nexus.endpoint "__user_service"
interface user-service {
resource user {
constructor(user-id: string, email: string);
update-email: func(email: string) -> user-result;
}
record get-user-request {
user-id: string,
}
type user-result = own<user>;
record update-email-request {
user-id: string,
email: string,
}
get-user: func(request: get-user-request) -> user-result;
update-email: func(request: update-email-request) -> user-result;
}Generate Python:
cargo run -- generate \
--lang python \
--input examples/inputs/user-service.wit \
--output /tmp/user_serviceGenerate TypeScript:
cargo run -- generate \
--lang typescript \
--input examples/inputs/user-service.wit \
--output /tmp/user-serviceAdd --format to run a formatter after generation:
- Python:
ruff format - TypeScript:
prettier --write
The user-service example is intentionally small and WIT-native. The type-showcase example demonstrates broader WIT type coverage: records, enums, flags, variants, results, maps, tuples, resources, resource methods, and an operation with no return value without proto annotations.
The WIT file defines the public surface. @nexus directives carry the parts WIT does not express directly:
- service endpoint names
- service wire names
- support file paths
- language-native override types
- flattened API-only field types
- sourced field expressions
- function conversion metadata
- output transforms
- explicit resource method operation bindings
- experimental service, operation, and record warnings
@nexus.delay-load-temporalio-workflowon Python services that must not importtemporalio.workflowuntil an operation executes
Resource methods bind to operations only when the method and operation have the same generated operation name. When they intentionally differ, mark the method with @nexus.operation, for example /// @nexus.operation "cancel-workflow" on cancel: func(...) to bind it to cancel-workflow: func(...).
Input WIT files can add support code with @nexus.support. Python support fragments are copied into the generated private _support package, and TypeScript support fragments are emitted as support.ts next to the generated index.ts.
The examples include small language runtimes that are not generated from WIT:
- Python:
examples/python/nex_gen_runtime.py - TypeScript:
examples/typescript/nex-gen-runtime.ts
These runtimes provide shared serialization helpers for WIT-direct values, including the json/nexus payload encoding used by the example tests to round-trip generated records and resources through real Temporal Nexus clients. The TypeScript examples also include nex-gen-payload-converter.cjs so the Temporal TypeScript SDK can load the same payload converter through its payloadConverterPath data-converter hook.
These files are intentionally example/runtime shims. They should eventually be removed once the corresponding Temporal SDKs provide native serialization support for Nexus API generator values and resources.
Proto backing is opt-in per WIT type. Use it when an operation should accept or return generated API models while converting to or from protobuf messages at the Nexus boundary.
Proto-backed WIT uses:
@nexus.protoon a WIT type to identify the protobuf message or enum it represents@nexus.proto-fieldwhen the WIT field name differs from the proto field name--descriptorsongenerateso the generator can validate fields and derive proto conversion code
Example:
package temporal:nexus@1.0.0;
world system {
export workflow-service;
}
/// @nexus.endpoint "__temporal_system"
/// @nexus.service-name "temporal.api.workflowservice.v1.WorkflowService"
interface workflow-service {
use nexus:temporal-types/model@1.0.0.{signal-function, task-queue, workflow-function};
/// @nexus.proto "temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionRequest"
record signal-with-start-workflow-request {
/// @nexus.proto-field "workflow_type"
workflow: workflow-function,
workflow-id: string,
task-queue: task-queue,
/// @nexus.proto-field "signal_name"
signal: signal-function,
/// @nexus.source "workflow_namespace"
namespace: option<string>,
}
/// @nexus.proto "temporal.api.workflowservice.v1.SignalWithStartWorkflowExecutionResponse"
record signal-with-start-workflow-response {
run-id: option<string>,
}
/// @nexus.output-transform
/// python-type="workflow.ExternalWorkflowHandle[typing.Any]"
/// python="workflow.get_external_workflow_handle(request.workflow_id, run_id=result.run_id)"
/// typescript-type="workflow.ExternalWorkflowHandle"
/// typescript="workflow.getExternalWorkflowHandle(request.workflowId, result.runId ?? undefined)"
/// @nexus.operation name="SignalWithStartWorkflowExecution"
signal-with-start-workflow: func(
request: signal-with-start-workflow-request,
) -> signal-with-start-workflow-response;
}Generate a proto-backed example:
cargo run -- generate \
--lang python \
--input examples/inputs/workflow-service.wit \
--input examples/inputs/deps \
--descriptors examples/descriptors/temporal_api.bin \
--output /tmp/workflow_service--descriptors can be passed more than once when a proto-backed API depends on multiple descriptor files. Duplicate files or duplicate symbols are rejected.
The examples include a reusable Temporal semantic/common type WIT input:
nexus:temporal-types/model@1.0.0
Pass it as an additional --input when generating an API that uses
nexus:temporal-types/model@1.0.0. For generate, the first --input is the
API generation root and later inputs are linked into the parser workspace. For
add-rpc, pass any WIT inputs needed to resolve shared types; when extending an
existing WIT file, put that file first. A linked input can be a single WIT file,
a WIT package directory, or a directory containing WIT package directories, so
examples/inputs/deps links every package under it.
Generate WIT for a proto RPC from a descriptor set:
cargo run -- add-rpc \
--descriptors examples/descriptors/temporal_api.bin \
--rpc SignalWithStartExecution \
--input examples/inputs/depsWrite the standalone WIT scaffold to a file instead of stdout:
cargo run -- add-rpc \
--descriptors examples/descriptors/temporal_api.bin \
--rpc temporal.api.workflowservice.v1.WorkflowService.SignalWithStartWorkflowExecution \
--input examples/inputs/deps \
--output /tmp/add-rpc.witExtend an existing WIT file with a new RPC:
cargo run -- add-rpc \
--descriptors examples/descriptors/temporal_api.bin \
--rpc SignalWorkflowExecution \
--input examples/inputs/workflow-service.wit \
--input examples/inputs/depsRewrite the existing WIT file in place by pointing --output at the same path:
cargo run -- add-rpc \
--descriptors examples/descriptors/temporal_api.bin \
--rpc SignalWorkflowExecution \
--input examples/inputs/workflow-service.wit \
--input examples/inputs/deps \
--output examples/inputs/workflow-service.witValidate the Python examples:
cargo build-examples --lang python
cd examples/python
uv run pytest
uv run basedpyrightValidate the TypeScript examples:
cargo build-examples --lang typescript
cd examples/typescript
npm install
npm run test
npm run typecheckcargo test validates the checked-in example outputs as-is. Use cargo build-examples when you want to refresh them.