Dependency injection for Nexus Service handlers
Nexus Service handlers support dependency injection through `Temporalio.Extensions.Hosting`. Register handlers using `AddScopedNexusService<T>`, `AddSingletonNexusService<T>`, or `AddTransientNexusService<T>` with the generic-host Worker. A new scoped handler instance and its scoped dependencies are created for each Operation invocation; dependencies are not cached between invocations. Handler dependencies are injected through the handler's constructor.
Dependency injection for Nexus Service handlers example
```csharp
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(ctx =>
ctx.
AddScoped<IGreetingClient, GreetingClient>().
AddHostedTemporalWorker(handlerTaskQueue).
ConfigureOptions(options => options.ClientOptions = LoadConnectOptions()).
AddScopedNexusService<GreetingServiceHandler>())
.Build();
await host.RunAsync();
```
Handler implementation:
```csharp
[NexusServiceHandler(typeof(IGreetingService))]
public class GreetingServiceHandler
{
private readonly IGreetingClient greetingClient;
public GreetingServiceHandler(IGreetingClient greetingClient) => this.greetingClient = greetingClient;
[NexusOperationHandler]
public IOperationHandler<IGreetingService.SayHelloInput, string> SayHello() =>
OperationHandler.Sync<IGreetingService.SayHelloInput, string>(
(ctx, input) => greetingClient.GetGreetingAsync(input.Name));
}
```
This example shows registering a Nexus Service handler with dependency injection using the generic host.
Synchronous Nexus Operation handler example
```csharp
using NexusRpc.Handlers;
[NexusServiceHandler(typeof(IHelloService))]
public class HelloService
{
[NexusOperationHandler]
public IOperationHandler<IHelloService.EchoInput, IHelloService.EchoOutput> Echo() =>
OperationHandler.Sync<IHelloService.EchoInput, IHelloService.EchoOutput>(
(ctx, input) => new(input.Message));
}
```
This example shows a simple synchronous handler that echoes the input message.
Query Workflow from sync Nexus handler
```csharp
private static string WorkflowIdForUser(string userId) => $"GreetingWorkflow_for_{userId}";
[NexusOperationHandler]
public IOperationHandler<INexusGreetingService.GetLanguagesInput, INexusGreetingService.GetLanguagesOutput> GetLanguages() =>
OperationHandler.Sync<INexusGreetingService.GetLanguagesInput, INexusGreetingService.GetLanguagesOutput>(
async (ctx, input) =>
{
var client = NexusOperationExecutionContext.Current.TemporalClient;
var handle = client.GetWorkflowHandle<GreetingWorkflow>(WorkflowIdForUser(input.UserId));
return await handle.QueryAsync(wf => wf.QueryLanguages(input.IncludeUnsupported));
});
```
This example shows accessing the Temporal Client from a Nexus operation context to query a Workflow. The Workflow ID is derived from the input userId.
Asynchronous Nexus Operation handler to start Workflow
Use `WorkflowRunOperationHandler.FromHandleFactory` to create asynchronous Nexus Operation handlers that start a Workflow. This is the easiest way to expose a Workflow as an operation. Asynchronous operations should be used when latency or availability is uncertain, work might exceed the 10-second deadline, or execution depends on potentially unreliable services. Workflow IDs should typically be business-meaningful and are used to dedupe Workflow starts; the operation's request ID can be used for this purpose.
Asynchronous Nexus Operation handler example
```csharp
using NexusRpc.Handlers;
using Temporalio.Nexus;
[NexusServiceHandler(typeof(IHelloService))]
public class HelloService
{
[NexusOperationHandler]
public IOperationHandler<IHelloService.HelloInput, IHelloService.HelloOutput> SayHello() =>
WorkflowRunOperationHandler.FromHandleFactory(
(WorkflowRunOperationContext context, IHelloService.HelloInput input) =>
context.StartWorkflowAsync(
(HelloHandlerWorkflow wf) => wf.RunAsync(input),
new() { Id = context.HandlerContext.RequestId }));
}
```
This example shows creating an asynchronous handler that starts a Workflow using the operation's request ID for the Workflow ID.
Map single Nexus input to multiple Workflow arguments
A Nexus Operation accepts only one input parameter. To pass multiple arguments to a Workflow, use different arguments in the `RunAsync` call. The Nexus input is unpacked and passed as separate arguments to the Workflow's run method.
Map Nexus input to multiple Workflow arguments example
```csharp
[NexusServiceHandler(typeof(IHelloService))]
public class HelloService
{
[NexusOperationHandler]
public IOperationHandler<IHelloService.HelloInput, IHelloService.HelloOutput> SayHello() =>
WorkflowRunOperationHandler.FromHandleFactory(
(WorkflowRunOperationContext context, IHelloService.HelloInput input) =>
context.StartWorkflowAsync(
(HelloHandlerWorkflow wf) => wf.RunAsync(input.Language, input.Name),
new() { Id = context.HandlerContext.RequestId }));
}
```
This example shows expanding a single Nexus Operation input (HelloInput) into two separate parameters (Language and Name) when starting the Workflow.
Register Nexus Service in Worker
Register a Nexus Service in a Worker using `AddNexusService()`. The handler Worker must also include the Workflow types that the handler starts. Use the `TemporalWorker` constructor with `TemporalWorkerOptions` to configure the task queue and add the Nexus Service and Workflows.
Register Nexus Service in Worker example
```csharp
async Task RunHandlerWorkerAsync()
{
logger.LogInformation("Running handler worker");
using var worker = new TemporalWorker(
await ConnectClientAsync("nexus-simple-handler-namespace"),
new TemporalWorkerOptions(taskQueue: "nexus-simple-handler-sample").
AddNexusService(new HelloService()).
AddWorkflow<HelloHandlerWorkflow>());
try
{
await worker.ExecuteAsync(tokenSource.Token);
}
catch (OperationCanceledException)
{
logger.LogInformation("Handler worker cancelled");
}
}
```
This example shows creating a handler Worker connected to the handler Namespace with a Nexus Service and handler Workflow registered.
Nexus Operation handler using temporalnexus.NewWorkflowRunOperation
Create a Nexus Operation handler by calling temporalnexus.NewWorkflowRunOperation with the operation name, handler workflow, and an Options function. The handler workflow bridges Nexus input types to the wrapped workflow's parameters. The Options function receives a context.Context, the operation input, and nexus.StartOperationOptions, and must return client.StartWorkflowOptions. Use options.RequestID to derive a stable Workflow ID for safe retries.
Nexus Operation registration with stable Workflow ID
var HelloOperation = temporalnexus.NewWorkflowRunOperation(HelloOperationName, HelloNexusWorkflow, func(ctx context.Context, input HelloInput, options nexus.StartOperationOptions) (client.StartWorkflowOptions, error) { return client.StartWorkflowOptions{ ID: options.RequestID, }, nil }) - This example shows how to create a workflow run operation with a callback that uses options.RequestID as the Workflow ID, ensuring stability across retries.
Nexus Operation handler workflow bridge example
HelloNexusWorkflow(ctx workflow.Context, input HelloInput) (string, error) { return SayHelloWorkflow(ctx, input.Name) } - This example shows how a handler workflow bridges Nexus input types to wrapped workflow parameters, extracting input.Name from the HelloInput struct to pass to SayHelloWorkflow.
Example: synchronous Nexus Operation handler
var EchoOperation = nexus.NewSyncOperation(service.EchoOperationName, func(ctx context.Context, input service.EchoInput, options nexus.StartOperationOptions) (service.EchoOutput, error) {
return service.EchoOutput(input), nil
})
Temporal Nexus handler helpers and builders
The temporalnexus package provides these builders for creating Nexus Operations: NewWorkflowRunOperation to run a Workflow as an asynchronous Nexus Operation, and GetClient to get the Temporal Client that the Worker was initialized with for synchronous handlers backed by Temporal primitives such as Signals and Queries.
Develop synchronous Nexus Operation handler with NewSyncOperation
The nexus.NewSyncOperation builder function is for exposing simple RPC handlers. Use temporalnexus.GetClient(ctx) to get the Temporal Client for signaling, querying, and listing Workflows. Implementations can also make other calls, but handlers should be reliable to avoid tripping the circuit breaker.
Develop asynchronous Nexus Operation handler with NewWorkflowRunOperation
Use the NewWorkflowRunOperation constructor to expose a Workflow as an asynchronous operation. This is the easiest way to expose a Workflow as a Nexus operation. Workflow IDs should typically be business-meaningful IDs and are used to deduplicate workflow starts.
Example: asynchronous Nexus Operation handler with NewWorkflowRunOperation
var HelloOperation = temporalnexus.NewWorkflowRunOperation(service.HelloOperationName, HelloHandlerWorkflow, func(ctx context.Context, input service.HelloInput, options nexus.StartOperationOptions) (client.StartWorkflowOptions, error) {
return client.StartWorkflowOptions{
ID: service.HelloWorkflowID(input),
}, nil
})
Map Nexus Operation input to multiple Workflow arguments
A Nexus Operation can only take one input parameter. If you want a Nexus Operation to start a Workflow that takes multiple arguments, use NewWorkflowRunOperationWithOptions or MustNewWorkflowRunOperationWithOptions, then use temporalnexus.ExecuteUntypedWorkflow to pass multiple arguments to the Workflow.
Example: map Nexus Operation input to multiple Workflow arguments
var HelloOperation = temporalnexus.MustNewWorkflowRunOperationWithOptions(temporalnexus.WorkflowRunOperationOptions[service.HelloInput, service.HelloOutput]{
Name: service.HelloOperationName,
Handler: func(ctx context.Context, input service.HelloInput, options nexus.StartOperationOptions) (temporalnexus.WorkflowHandle[service.HelloOutput], error) {
return temporalnexus.ExecuteUntypedWorkflow[service.HelloOutput](
ctx,
options,
client.StartWorkflowOptions{
ID: service.HelloWorkflowID(input),
},
HelloHandlerWorkflow,
input.Name,
input.Language,
)
},
})
Register Nexus Service in a Worker
Create a nexus.NewService with the service name, register operations using service.Register(), then register the service in the Worker using w.RegisterNexusService(service). Also register any Workflows that the operations will start.
Example: register Nexus Service in Worker
w := worker.New(c, taskQueue, worker.Options{})
service := nexus.NewService(service.HelloServiceName)
err = service.Register(handler.EchoOperation, handler.HelloOperation)
if err != nil {
log.Fatalln("Unable to register operations", err)
}
w.RegisterNexusService(service)
w.RegisterWorkflow(handler.HelloHandlerWorkflow)
err = w.Run(worker.InterruptCh())
Asynchronous Nexus Operation handler with WorkflowRunOperation.fromWorkflowMethod
Use WorkflowRunOperation.fromWorkflowMethod to expose a Workflow as an asynchronous operation. This is the easiest way to expose a Workflow as an operation. Workflow IDs should typically be business-meaningful IDs and are used to dedupe Workflow starts. Use the request ID allocated by Temporal for deduplication, or pass the ID in the Operation input as part of the Nexus Service contract.
Synchronous Nexus Operation handler with OperationHandler.sync
Create synchronous RPC handlers using OperationHandler.sync method. Use Nexus.getOperationContext().getWorkflowClient(ctx) to get the Temporal Client for signaling, querying, and listing Workflows. Implementations can also make other calls, but handlers should be reliable to avoid tripping the circuit breaker. The example: `return OperationHandler.sync((ctx, details, input) -> new SampleNexusService.EchoOutput(input.getMessage()));`
Map Nexus Operation input to multiple Workflow arguments
When a Nexus Operation needs to start a Workflow that takes multiple arguments, use WorkflowRunOperation.fromWorkflowHandle method instead of fromWorkflowMethod. This allows mapping a single Nexus Operation input parameter to multiple Workflow arguments using WorkflowHandle.fromWorkflowMethod.
Register Nexus Service in a Worker
Register a Nexus Service in a Worker using worker.registerNexusServiceImplementation(new SampleNexusServiceImpl()). The service implementation class should be annotated with @ServiceImpl(service = SampleNexusService.class) and contain methods annotated with @OperationImpl that return OperationHandler instances corresponding to operations defined in the service interface.
WorkflowRunOperation bridges Nexus input to Workflow method directly
WorkflowRunOperation.fromWorkflowMethod() creates an asynchronous Nexus Operation by bridging the Nexus Operation's input type directly to the Workflow method's parameter type. The handler uses Nexus.getOperationContext().getWorkflowClient() to access the WorkflowClient and start a new Workflow instance for each Operation request.
Nexus Operation Handler implementation with @ServiceImpl and @OperationImpl
Nexus Operation Handlers are implemented in a class annotated with @ServiceImpl(service = SayHelloNexusService.class). Each handler method is annotated with @OperationImpl and returns an OperationHandler<InputType, OutputType>. The handler contains the logic executed when a caller invokes the Nexus Operation. Handlers can use WorkflowRunOperation.fromWorkflowMethod() to wrap an existing Temporal Workflow, making it callable as a Nexus Operation.
Example: Nexus Operation Handler wrapping a Workflow
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
);
}
}
This example implements a Nexus Operation Handler that wraps SayHelloWorkflow as a Nexus Operation, starting a new Workflow instance for each request with a unique WorkflowId.
Map Nexus Operation input to multiple Workflow arguments
A Nexus Operation can only take one input parameter. To map a Nexus Operation to a Workflow that takes multiple arguments, use the `ctx.start_workflow` method with the `args` parameter:
```python
@nexusrpc.handler.service_handler(service=MyNexusService)
class MyNexusServiceHandler:
@nexus.workflow_run_operation
async def hello(
self, ctx: nexus.WorkflowRunOperationContext, input: HelloInput
) -> nexus.WorkflowHandle[HelloOutput]:
return await ctx.start_workflow(
HelloHandlerWorkflow.run,
args=[
input.name, # First argument: name
input.language, # Second argument: language
],
id=f"hello-multi-args-{input.name}-{input.language}",
)
```
Nexus exception types in Python
Python provides three Nexus-specific exception classes:
1. `nexusrpc.OperationError` - Raise this in a Nexus operation to indicate it has failed according to its own application logic and should not be retried.
2. `nexusrpc.HandlerError` - Raise this with a specific HandlerErrorType. Non-retryable types: BAD_REQUEST, UNAUTHENTICATED, UNAUTHORIZED, NOT_FOUND, NOT_IMPLEMENTED. Retryable types: RESOURCE_EXHAUSTED, INTERNAL, UNAVAILABLE, UPSTREAM_TIMEOUT.
3. `temporalio.exceptions.NexusOperationError` - Raised inside a Workflow when a Nexus operation fails for any reason. Use the `__cause__` attribute to access the cause chain.
Synchronous Nexus Operation handler with @nexusrpc.handler.sync_operation
Create a synchronous operation handler using the @nexusrpc.handler.sync_operation decorator:
```python
import nexusrpc
@nexusrpc.handler.service_handler(service=MyNexusService)
class MyNexusServiceHandler:
@nexusrpc.handler.sync_operation
async def my_sync_operation(
self, ctx: nexusrpc.handler.StartOperationContext, input: MyInput
) -> MyOutput:
return MyOutput(message=f"Hello {input.name} from sync operation!")
```
A synchronous operation handler must return quickly (less than 10 seconds). Implementations can make other calls but handlers should be reliable to avoid tripping the circuit breaker.
Asynchronous Nexus Operation handler with @nexus.workflow_run_operation
Create an asynchronous Nexus Operation handler using the @nexus.workflow_run_operation decorator to easily expose a Workflow as an operation:
```python
import nexusrpc
from temporalio import nexus
@nexusrpc.handler.service_handler(service=MyNexusService)
class MyNexusServiceHandler:
@nexus.workflow_run_operation
async def my_workflow_run_operation(
self, ctx: nexus.WorkflowRunOperationContext, input: MyInput
) -> nexus.WorkflowHandle[MyOutput]:
return await ctx.start_workflow(
WorkflowStartedByNexusOperation.run,
input,
id=str(uuid.uuid4()),
)
```
Workflow IDs should typically be business-meaningful IDs used to dedupe Workflow starts. The ID should generally be passed in the Operation input as part of the Nexus Service contract.
Register Nexus Service handler in a Worker
Register a Nexus Service handler in a Worker by passing it to the `nexus_service_handlers` parameter:
```python
async def main():
client = await Client.connect("localhost:7233", namespace=NAMESPACE)
worker = Worker(
client,
task_queue=TASK_QUEUE,
workflows=[WorkflowStartedByNexusOperation],
nexus_service_handlers=[MyNexusServiceHandler()],
)
await worker.run()
```
You can pass any arguments you need to your service handler's `__init__` method when instantiating it.
Complete Python Nexus quickstart example - operation handler
To implement a Nexus Operation handler in a file called handler.py:
```python
import uuid
import nexusrpc.handler
from temporalio import nexus
from service import MyInput, SayHelloNexusService
from workflows import SayHelloWorkflow
@nexusrpc.handler.service_handler(service=SayHelloNexusService)
class SayHelloNexusServiceHandler:
@nexus.workflow_run_operation
async def say_hello(
self, ctx: nexus.WorkflowRunOperationContext, input: MyInput
) -> nexus.WorkflowHandle[str]:
return await ctx.start_workflow(
SayHelloWorkflow.run,
input.name,
id=f"say-hello-nexus-{uuid.uuid4()}",
)
```
Implement Nexus Operation handler with @nexusrpc.handler.service_handler decorator
Implement operation handlers using the @nexusrpc.handler.service_handler(service=YourService) decorator on a handler class. Each handler method decorated with @nexus.workflow_run_operation is an async function that receives a WorkflowRunOperationContext and input parameters, and returns a WorkflowHandle of the result type.
Use @nexus.workflow_run_operation to start workflows from Nexus handlers
The @nexus.workflow_run_operation decorator creates an asynchronous Nexus Operation that starts a Workflow. The handler receives ctx: nexus.WorkflowRunOperationContext and can call ctx.start_workflow(WorkflowClass.run, args, id=...) to start the workflow and return a WorkflowHandle[ResultType].
Task queue default behavior in WorkflowRunOperationHandler
In WorkflowRunOperationHandler, when starting a Workflow using temporalNexus.startWorkflow(), the task queue defaults to the task queue that the Operation handler is running on if not explicitly specified.
WorkflowRunOperationHandler for starting workflows from Nexus
The temporalNexus.WorkflowRunOperationHandler<InputType, OutputType> is used to create an asynchronous Nexus Operation that starts a Workflow. It is instantiated with an async handler function that receives a context and input, and returns the workflow result. The handler can use temporalNexus.startWorkflow() to start a workflow with specific arguments and configuration.
Nexus Operation request handler timeout
A handler has less than 10 seconds to process a start or cancel request.
Asynchronous Nexus Operation handler using WorkflowRunOperationHandler
Use `@temporalio/nexus`'s `WorkflowRunOperationHandler` helper class to expose a Temporal Workflow as an asynchronous Nexus Operation. The handler receives a function that can validate/transform input before passing it to the Workflow. Call `temporalNexus.startWorkflow()` to start the Workflow. Even though a Nexus operation takes one input parameter, multiple arguments can be passed to the workflow by using multiple properties of the input object in the `args` array.
Handler reliability and circuit breaker in Nexus
Handlers should be reliable since the circuit breaker trips after 5 consecutive retryable errors, blocking all Operations from the caller to that Endpoint. Use synchronous Nexus Operations only when execution is highly reliable with predictably low latency and finishes within the 10-second handler deadline. Use asynchronous operations when latency or availability is uncertain, work might exceed the handler deadline, or execution depends on potentially unreliable services.
Synchronous Nexus Operation handler implementation
Implement a synchronous Nexus Operation handler as a simple async function using `nexus.serviceHandler()`. Use `temporalNexus.getClient()` from @temporalio/nexus to get the Temporal Client for signaling, querying, and listing Workflows. Synchronous handlers should only be used for highly reliable operations with predictably low latency that complete well within the 10-second handler deadline.
Use Temporal Client in Nexus handler for Signals and Queries
Within a synchronous Nexus Operation handler, use the Temporal Client obtained from `temporalNexus.getClient()` to signal, query, or update Workflows. All calls must complete within the Nexus request timeout (10 seconds). The handler receives `ctx.abortSignal` that triggers when the deadline is exceeded — pass it to Temporal Client calls to ensure cancellation. Use `ctx.requestDeadline` as an optional Date to make decisions about whether to start work that may not finish in time.
Workflow IDs for Nexus Operations should be business-meaningful
Workflow IDs used in Nexus Operations should typically be business-meaningful IDs and are used to deduplicate Workflow starts. In general, the ID should be passed in the Operation input as part of the Nexus Service contract. For example, workflow IDs can be derived from client IDs or other identifiers provided in the operation input.