Skip to main content

Generate code from the contract

View Markdown

Generate code from the contract before writing any implementation. Both sides use it: the handler implements the generated Service definition, and the caller invokes Operations through it.

What generation produces​

For each type in the contract, NexGen emits a typed model and a runtime validator. For each Service, it also emits a Nexus Service definition with one member per Operation, in the form that is idiomatic for the language.

  • The handler implements the Service definition, and the Worker registers that implementation.
  • The caller invokes Operations through it, so calls are type-checked against the contract.

The validators run when a payload is parsed and again when it is serialized, so a request that violates the contract is rejected at the boundary instead of reaching your Workflow. Violations aggregate into one error naming every field that failed, which a handler maps to BAD_REQUEST.

Generate the code​

Generation is one command per language. See Usage in the nexgen README for each language's flags.

Installing nexgen

Every nexgen release is published as a pre-release, so GitHub's "latest release" URL returns 404. Pick a version from the releases list instead, and check that it has binaries attached before you download it. Some releases have none.

Run this command from the root of the samples-java clone you made in Before you start. It reads approval.nexusrpc.yaml, the contract from step 1.

Run: generate the Java code
nexgen java \
--output core/src/main/java/io/temporal/samples/nexuswalkthrough/generatedservice \
--package-name io.temporal.samples.nexuswalkthrough.generatedservice \
core/src/main/java/io/temporal/samples/nexuswalkthrough/approval.nexusrpc.yaml

Java requires the package name's last segment to match the output directory's name, which is why both end in generatedservice. The rest of each path is this repository's layout. In your own project, point --output and --package-name wherever your source tree keeps generated code.

Generation overwrites the files it emits but doesn't remove anything else, so a type you delete from the contract leaves its generated class behind, still compiling and still importable. Delete those by hand after a type goes away, and keep the contract outside the output directory so it stays easy to tell the two apart.

Commit the generated code and regenerate whenever the contract changes. Don't edit the generated files, because the next run overwrites them. When a generated name is wrong for your language, fix it in the contract with a per-language naming override. See Naming & overrides in the nexgen README.

One contract, four languages​

Run the generator once per language to get that language's typed models, validators, and Service definition. You still write the handler and the caller yourself, but the part both sides must agree on comes from one source. A handler doesn't need to know which languages its callers use, and a caller doesn't need to know which language implements the handler.

That is what lets the cross-language call in step 6 work with no coordination beyond the contract.

RESOURCES
  • NexGen for what the generator produces.
  • The nexgen README for installation, per-language commands, and the supported JSON Schema subset.
  • Data validation for how the generated validators behave.

Next step​

Step 3: Choose a backing

Workflow, Standalone Activity, Update, or sync messaging, one per Operation.

→