Standalone Nexus Operations - Java SDK
Standalone Nexus Operations let you run Nexus Operation Executions independently, without
being orchestrated by a Workflow. Instead of calling a Nexus Operation from within a Workflow Definition using
Workflow.newNexusServiceStub(), you execute a Standalone Nexus Operation directly from a Nexus service client created
from a NexusClient using NexusClient.newNexusServiceClient().
Standalone Nexus Operations use the same Nexus Service contract, Operation handlers, and Worker setup as Workflow-driven Operations — only the execution path differs. See the Nexus feature guide for details on defining a Service contract, developing Operation handlers, and registering a Service in a Worker.
This page focuses on the client-side APIs that are unique to Standalone Nexus Operations:
- Execute a Standalone Nexus Operation
- Start a Standalone Nexus Operation and Wait for the Result
- List Standalone Nexus Operations
- Count Standalone Nexus Operations
- Run Standalone Nexus Operations with Temporal Cloud
This documentation uses source code from the Java Nexus Standalone sample.
Prerequisites
Standalone Nexus Operations are at Pre-release and require a special Temporal CLI build.
1. Install and verify the Pre-release Temporal CLI
The temporal nexus operation commands require a Pre-release build of the Temporal CLI. See
Temporal CLI support for the platform downloads,
then verify:
./temporal --version
# temporal version 1.7.4-standalone-nexus-operations
Run it as ./temporal from the directory where you extracted it. The standard brew install temporal
build does not include Standalone Nexus Operation support during Pre-release.
2. Start a local dev server
The Pre-release dev server enables Standalone Nexus Operations by default — no dynamic config is required. Start it with the caller and handler Namespaces pre-created:
./temporal server start-dev \
--namespace my-caller-namespace \
--namespace my-handler-namespace
The starter and Worker connect to two different Namespaces (a caller Namespace and a handler Namespace), mirroring how Nexus crosses Namespace boundaries.
To run the examples on this page against the Java sample, create a Nexus Endpoint that routes to the handler Namespace and the Worker's Task Queue:
./temporal operator nexus endpoint create \
--name my-nexus-endpoint \
--target-namespace my-handler-namespace \
--target-task-queue nexus-handler-queue
Start the sample handler Worker in the handler Namespace:
TEMPORAL_NAMESPACE=my-handler-namespace \
./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusstandalone.handler.HandlerWorker
Run the starter in the caller Namespace (from a separate terminal):
TEMPORAL_NAMESPACE=my-caller-namespace \
./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusstandalone.StandaloneClientStarter
Execute a Standalone Nexus Operation
To execute a Standalone Nexus Operation, first create a
NexusClient, then
derive a typed
NexusServiceClient
from it with newNexusServiceClient(), bound to a specific Nexus Endpoint and Service. The endpoint must be
pre-created on the server. Then call start() or execute() from application code (for example, a starter program),
not from inside a Workflow Definition.
execute() waits for the Operation to complete and returns the result.
Both methods take a StartNexusOperationOptions
whose id is required — the SDK never generates one for you. scheduleToCloseTimeout is optional and defaults to the
maximum allowed by the Temporal server.
NexusClient nexusClient = NexusClient.newInstance(stubs, options);
NexusServiceClient<GreetingNexusService> greetingClient =
nexusClient.newNexusServiceClient(GreetingNexusService.class, ENDPOINT_NAME);
// Block until the operation completes and return its result.
GreetingOutput greeting =
greetingClient.execute(
GreetingNexusService::greet,
StartNexusOperationOptions.newBuilder()
.setId("greet-" + UUID.randomUUID())
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
.build(),
new GreetingInput("World"));
executeAsync() is the same but returns a CompletableFuture instead of blocking.
CompletableFuture<GreetingOutput> future =
greetingClient.executeAsync(
GreetingNexusService::greet, options, new GreetingInput("World"));
GreetingOutput greeting = future.get();
See the full starter sample for a complete example that executes both synchronous and asynchronous Operations, gets their results, and lists and counts Operations.
Or use the Temporal CLI to execute a Standalone Nexus Operation:
./temporal nexus operation execute \
--endpoint my-nexus-endpoint \
--service GreetingNexusService \
--operation greet \
--operation-id my-greet-op \
--input '{"name":"World"}'
Start a Standalone Nexus Operation and Wait for the Result
start() returns a
NexusOperationHandle.
Use NexusOperationHandle.getResult() to wait until the Operation completes and retrieve its result. This works for
both synchronous and asynchronous Operations.
// Start an operation and get a NexusOperationHandle.
NexusOperationHandle<GreetingOutput> handle =
greetingClient.start(
GreetingNexusService::startGreeting, options, new GreetingInput("World"));
// Block until the operation completes and retrieve its result.
GreetingOutput greeting = handle.getResult();
If the Operation completed successfully, the result is returned. If the Operation failed, the failure is thrown as a
NexusOperationException. Use getResultAsync() for a non-blocking CompletableFuture, or
getResult(long timeout, TimeUnit unit) to bound the wait.
Or use the Temporal CLI to wait for a result by Operation ID:
./temporal nexus operation result --operation-id my-greet-op
List Standalone Nexus Operations
Use NexusClient.listNexusOperationExecutions()
to list Standalone Nexus Operation Executions that match a List Filter query. The result is a Stream
of operation metadata entries.
Note that listNexusOperationExecutions() is called on a NexusClient, not on the typed NexusServiceClient.
String query = "Endpoint = \"" + ENDPOINT_NAME + "\"";
nexusClient
.listNexusOperationExecutions(query)
.forEach(
op ->
System.out.printf(
"OperationId: %s, Operation: %s, Status: %s%n",
op.getOperationId(), op.getOperation(), op.getStatus()));
The query parameter accepts List Filter syntax. For example,
"Endpoint = 'my-endpoint' AND ExecutionStatus = 'Running'".
Or use the Temporal CLI:
./temporal nexus operation list --query 'Endpoint = "my-nexus-endpoint"'
Count Standalone Nexus Operations
Use NexusClient.countNexusOperationExecutions()
to count Standalone Nexus Operation Executions that match a List Filter query.
Note that countNexusOperationExecutions() is called on a NexusClient, not on the typed NexusServiceClient.
String query = "Endpoint = \"" + ENDPOINT_NAME + "\"";
NexusOperationExecutionCount count = nexusClient.countNexusOperationExecutions(query);
System.out.println("Total Nexus operations: " + count.getCount());
Passing a GROUP BY query (for example, "GROUP BY ExecutionStatus") returns a count per group, available through
NexusOperationExecutionCount.getGroups().
Or use the Temporal CLI:
./temporal nexus operation count --query 'Endpoint = "my-nexus-endpoint"'
Run Standalone Nexus Operations with Temporal Cloud
The code samples referenced on this page build their client from a ClientConfigProfile loaded from a TOML profile, so
the same code works against Temporal Cloud — just point the profile at your Cloud Namespace (or override the connection
via TEMPORAL_* environment variables). No code changes are needed.
For full details on connecting to Temporal Cloud, including Namespace creation, Nexus Endpoint setup, certificate generation, and authentication options, see Make Nexus calls across Namespaces in Temporal Cloud and Connect to Temporal Cloud.