Nexus Java Quickstart
Temporal Nexus connects Temporal Applications within and across Namespaces using a Nexus Endpoint, a Nexus Service contract, and Nexus Operations. Build a Nexus Service that wraps an existing Temporal Workflow, then invoke it from a caller Workflow.
This page will help you get a working sample running in Java. To evaluate whether Nexus fits your use case, see the evaluation guide and to learn more about Nexus features, click here.
Prerequisites: Complete the Java SDK Quickstart first.
You should have SayHelloWorkflow, SayHelloWorkflowImpl, GreetActivities, GreetActivitiesImpl, and SayHelloWorker from that guide.
What you'll build
You have SayHelloWorkflow running in the default Namespace.
By the end of this guide:
- A Nexus Service will expose
SayHelloWorkflowas an Operation. - A second Namespace will contain a Workflow that calls that Operation.
- The caller Workflow will get back
"Hello Temporal"— the same result, but across Namespaces.
1. Define the Nexus Service
Create a file called SayHelloNexusService.java that defines the Nexus Service contract.
Creating a Nexus Service establishes the contract between your implementation and any callers. It provides type safety when invoking Nexus Operations and ensures that Operation Handlers fulfill the contract.
The @Service annotation declares this as a Nexus Service.
The @Operation annotation marks sayHello as a callable Nexus Operation.
SayHelloWorkflow returns String, so the operation output type is also String.
You can also define your Nexus Service contract using an IDL and generate the Python service definitions with nexus-rpc-gen.
package helloworkflow;
import io.nexusrpc.Operation;
import io.nexusrpc.Service;
@Service
public interface SayHelloNexusService {
@Operation
String sayHello(String name);
}
2. Define the Nexus Operation Handlers
Create a file called SayHelloNexusServiceImpl.java that implements the Nexus Operation handler.
Operation handlers contain the logic that runs when a caller invokes a Nexus Operation.
The @OperationImpl annotation creates an asynchronous Nexus Operation that starts SayHelloWorkflow.
The handler bridges the Nexus String input to SayHelloWorkflow's sayHello method directly.
package helloworkflow;
import io.nexusrpc.handler.OperationHandler;
import io.nexusrpc.handler.OperationImpl;
import io.nexusrpc.handler.ServiceImpl;
import io.temporal.client.WorkflowOptions;
import io.temporal.nexus.Nexus;
import io.temporal.nexus.WorkflowRunOperation;
@ServiceImpl(service = SayHelloNexusService.class)
public class SayHelloNexusServiceImpl {
@OperationImpl
public OperationHandler<String, String> sayHello() {
return WorkflowRunOperation.fromWorkflowMethod(
(ctx, details, name) ->
Nexus.getOperationContext()
.getWorkflowClient()
.newWorkflowStub(
SayHelloWorkflow.class,
WorkflowOptions.newBuilder()
.setWorkflowId("say-hello-nexus-" + details.getRequestId())
.build())
::sayHello
);
}
}
3. Register the Nexus Service Handler in a Worker
Update your existing SayHelloWorker.java to register the Nexus Service Handler.
A Worker will only poll for and process incoming Nexus requests if the Nexus Service Handlers are registered. This is the same Worker concept used for Workflows and Activities.
The registerNexusServiceImplementation parameter registers the handler so it can receive Nexus Operation requests.
package helloworkflow;
import io.temporal.client.WorkflowClient;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
public class SayHelloWorker {
public static void main(String[] args) {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
WorkerFactory factory = WorkerFactory.newInstance(client);
Worker worker = factory.newWorker("my-task-queue");
worker.registerWorkflowImplementationTypes(SayHelloWorkflowImpl.class);
worker.registerActivitiesImplementations(new GreetActivitiesImpl());
worker.registerNexusServiceImplementation(new SayHelloNexusServiceImpl());
System.out.println("Starting SayHelloWorker for task queue 'my-task-queue'...");
factory.start();
}
}
4. Develop the caller Workflow
Create two files — NexusCallerWorkflow.java and NexusCallerWorkflowImpl.java — that define a Workflow which invokes the Nexus Operation.
The caller Workflow demonstrates the consumer side of Nexus. Instead of importing handler code directly, the caller only depends on the Service contract. This keeps the caller and handler decoupled so they can live in separate Namespaces, repositories, or even teams.
The Workflow.newNexusServiceStub method creates a client bound to your Nexus Service and Endpoint.
package helloworkflow;
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;
@WorkflowInterface
public interface NexusCallerWorkflow {
@WorkflowMethod
String greetThroughNexus(String name);
}
package helloworkflow;
import io.temporal.workflow.NexusOperationOptions;
import io.temporal.workflow.NexusServiceOptions;
import io.temporal.workflow.Workflow;
import java.time.Duration;
public class NexusCallerWorkflowImpl implements NexusCallerWorkflow {
private final SayHelloNexusService nexusService = Workflow.newNexusServiceStub(
SayHelloNexusService.class,
NexusServiceOptions.newBuilder()
.setOperationOptions(
NexusOperationOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
.build())
.build()
);
@Override
public String greetThroughNexus(String name) {
return nexusService.sayHello(name);
}
}
5. Create the caller Namespace and Nexus Endpoint
Before running the application, create a caller Namespace and a Nexus Endpoint to route requests from the caller to the handler.
The handler uses the default Namespace that was created when you started the dev server.
Namespaces provide isolation between the caller and handler sides. The Nexus Endpoint acts as a routing layer that connects the caller Namespace to the handler's target Namespace and Task Queue.
Make sure your local Temporal dev server is running (temporal server start-dev).
temporal operator namespace create --namespace my-caller-namespace
temporal operator nexus endpoint create \
--name my-nexus-endpoint-name \
--target-namespace default \
--target-task-queue my-task-queue
6. Run and Verify
Create NexusCallerStarter.java to start the caller Worker and execute the Workflow.
This brings everything together: the caller Worker hosts NexusCallerWorkflow, which uses the Nexus stub to invoke sayHello on the handler side.
The full request flows from the caller Workflow, through the Nexus Endpoint, to the handler Worker running SayHelloWorkflow, and back to the caller.
Run the application:
- Start the handler Worker in one terminal:
mvn compile exec:java \
-Dexec.mainClass="helloworkflow.SayHelloWorker"
- Run the caller in another terminal:
mvn compile exec:java \
-Dexec.mainClass="helloworkflow.NexusCallerStarter"
You should see:
Workflow result: Hello Temporal
Open the Temporal Web UI and switch between Namespaces to see both Workflow Executions.
In my-caller-namespace, find the NexusCallerWorkflow execution — you should see NexusOperationScheduled, NexusOperationStarted, and NexusOperationCompleted events in its history.
In default, find the SayHelloWorkflow execution that was started by the Nexus Operation.
package helloworkflow;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowClientOptions;
import io.temporal.client.WorkflowOptions;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import io.temporal.worker.WorkflowImplementationOptions;
import io.temporal.workflow.NexusServiceOptions;
import java.util.Collections;
public class NexusCallerStarter {
public static void main(String[] args) {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service,
WorkflowClientOptions.newBuilder()
.setNamespace("my-caller-namespace")
.build()
);
WorkerFactory factory = WorkerFactory.newInstance(client);
Worker worker = factory.newWorker("my-caller-task-queue");
worker.registerWorkflowImplementationTypes(
WorkflowImplementationOptions.newBuilder()
.setNexusServiceOptions(
Collections.singletonMap(
"SayHelloNexusService",
NexusServiceOptions.newBuilder()
.setEndpoint("my-nexus-endpoint-name")
.build()))
.build(),
NexusCallerWorkflowImpl.class
);
factory.start();
NexusCallerWorkflow workflow = client.newWorkflowStub(
NexusCallerWorkflow.class,
WorkflowOptions.newBuilder()
.setTaskQueue("my-caller-task-queue")
.setWorkflowId("nexus-caller-workflow-id")
.build()
);
String result = workflow.greetThroughNexus("Temporal");
System.out.println("Workflow result: " + result);
factory.shutdown();
System.exit(0);
}
}
Next Steps
Now that you have a working Nexus Service, here are some resources to deepen your understanding:
- Java Nexus Feature Guide: Covers synchronous and asynchronous Operations, error handling, cancellation, and cross-Namespace calls.
- Nexus Operations: The full Operation lifecycle, including retries, timeouts, and execution semantics.
- Nexus Services: Designing Service contracts and registering multiple Services per Worker.
- Nexus Patterns: Comparing the collocated and router-queue deployment patterns.
- Error Handling in Nexus: Handling retryable and non-retryable errors across caller and handler boundaries.
- Execution Debugging: Bi-directional linking and OpenTelemetry tracing for debugging Nexus calls.
- Nexus Endpoints: Managing Endpoints and understanding how they route requests.
- Temporal Nexus on Temporal Cloud: Deploying Nexus in a production Temporal Cloud environment with built-in access controls and multi-region connectivity.