From b7a698236681d0ead32fb6075360213d24d67c73 Mon Sep 17 00:00:00 2001 From: Joshua Frenchwood Date: Mon, 24 Aug 2026 15:09:33 -0500 Subject: [PATCH 1/3] Adding nexus TemporalOperationHandler to nexus-messaging examples --- nexus-messaging/src/callerpattern/README.md | 4 +- .../src/callerpattern/service/handler.ts | 48 +++++++++++-------- .../src/ondemandpattern/service/handler.ts | 48 +++++++++++-------- 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/nexus-messaging/src/callerpattern/README.md b/nexus-messaging/src/callerpattern/README.md index 89ea339bf..b746e7849 100644 --- a/nexus-messaging/src/callerpattern/README.md +++ b/nexus-messaging/src/callerpattern/README.md @@ -1,9 +1,9 @@ ## Caller pattern The handler worker starts a `GreetingWorkflow` for a user ID at boot. -`NexusGreetingService` holds that ID and routes every Nexus operation to it. +`nexusGreetingServiceHandler` derives the workflow ID and routes every Nexus operation to it. The caller's input does not have that workflow ID as the caller doesn't know it -- but the caller sends in the User ID, -and `NexusGreetingService` knows how to get the desired workflow ID from that User ID (via the `GreetingWorkflow_for_` prefix). +and `nexusGreetingServiceHandler` knows how to get the desired workflow ID from that User ID (via the `GreetingWorkflow_for_` prefix). The handler worker uses the same prefix to generate a workflow ID from a user ID when it launches the workflow. diff --git a/nexus-messaging/src/callerpattern/service/handler.ts b/nexus-messaging/src/callerpattern/service/handler.ts index 837caedc9..6c3846f55 100644 --- a/nexus-messaging/src/callerpattern/service/handler.ts +++ b/nexus-messaging/src/callerpattern/service/handler.ts @@ -8,27 +8,35 @@ function workflowIdForUser(userId: string): string { } export const nexusGreetingServiceHandler = nexus.serviceHandler(nexusGreetingService, { - getLanguages: async (ctx, input: GetLanguagesInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(workflowIdForUser(input.userId)); - return await handle.query(getLanguagesQuery); - }, + getLanguages: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: GetLanguagesInput) { + const handle = client.client.workflow.getHandle(workflowIdForUser(input.userId)); + const result = await handle.query(getLanguagesQuery); + return temporalNexus.TemporalOperationResult.sync(result); + }, + }), - getLanguage: async (ctx, input: GetLanguageInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(workflowIdForUser(input.userId)); - return await handle.query(getLanguageQuery); - }, + getLanguage: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: GetLanguageInput) { + const handle = client.client.workflow.getHandle(workflowIdForUser(input.userId)); + const result = await handle.query(getLanguageQuery); + return temporalNexus.TemporalOperationResult.sync(result); + }, + }), - setLanguage: async (ctx, input: SetLanguageInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(workflowIdForUser(input.userId)); - return await handle.executeUpdate(setLanguageUpdate, { args: [input.language] }); - }, + setLanguage: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: SetLanguageInput) { + const handle = client.client.workflow.getHandle(workflowIdForUser(input.userId)); + const result = await handle.executeUpdate(setLanguageUpdate, { args: [input.language] }); + return temporalNexus.TemporalOperationResult.sync(result); + }, + }), - approve: async (ctx, input: ApproveInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(workflowIdForUser(input.userId)); - await handle.signal(approveSignal); - }, + approve: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: ApproveInput) { + const handle = client.client.workflow.getHandle(workflowIdForUser(input.userId)); + await handle.signal(approveSignal); + return temporalNexus.TemporalOperationResult.sync(undefined); + }, + }), }); diff --git a/nexus-messaging/src/ondemandpattern/service/handler.ts b/nexus-messaging/src/ondemandpattern/service/handler.ts index d83145ed7..d2084eb96 100644 --- a/nexus-messaging/src/ondemandpattern/service/handler.ts +++ b/nexus-messaging/src/ondemandpattern/service/handler.ts @@ -27,27 +27,35 @@ export const nexusRemoteGreetingServiceHandler = nexus.serviceHandler(nexusRemot }, ), - getLanguages: async (ctx, input: GetLanguagesInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(getWorkflowId(input.userId)); - return await handle.query(getLanguagesQuery); - }, + getLanguages: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: GetLanguagesInput) { + const handle = client.client.workflow.getHandle(getWorkflowId(input.userId)); + const result = await handle.query(getLanguagesQuery); + return temporalNexus.TemporalOperationResult.sync(result); + }, + }), - getLanguage: async (ctx, input: GetLanguageInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(getWorkflowId(input.userId)); - return await handle.query(getLanguageQuery); - }, + getLanguage: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: GetLanguageInput) { + const handle = client.client.workflow.getHandle(getWorkflowId(input.userId)); + const result = await handle.query(getLanguageQuery); + return temporalNexus.TemporalOperationResult.sync(result); + }, + }), - setLanguage: async (ctx, input: SetLanguageInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(getWorkflowId(input.userId)); - return await handle.executeUpdate(setLanguageUpdate, { args: [input.language] }); - }, + setLanguage: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: SetLanguageInput) { + const handle = client.client.workflow.getHandle(getWorkflowId(input.userId)); + const result = await handle.executeUpdate(setLanguageUpdate, { args: [input.language] }); + return temporalNexus.TemporalOperationResult.sync(result); + }, + }), - approve: async (ctx, input: ApproveInput) => { - const client = temporalNexus.getClient(); - const handle = client.workflow.getHandle(getWorkflowId(input.userId)); - await handle.signal(approveSignal); - }, + approve: new temporalNexus.TemporalOperationHandler({ + async start(_ctx, client, input: ApproveInput) { + const handle = client.client.workflow.getHandle(getWorkflowId(input.userId)); + await handle.signal(approveSignal); + return temporalNexus.TemporalOperationResult.sync(undefined); + }, + }), }); From 365dc24943515cb2d0dc5adbeb873b69d9d318a3 Mon Sep 17 00:00:00 2001 From: Joshua Frenchwood Date: Tue, 25 Aug 2026 17:38:51 -0500 Subject: [PATCH 2/3] Adding Async Update --- nexus-messaging/src/callerpattern/README.md | 18 +++++++++++------- .../src/callerpattern/service/handler.ts | 5 ++--- nexus-messaging/src/ondemandpattern/README.md | 18 +++++++++++------- .../src/ondemandpattern/service/handler.ts | 5 ++--- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/nexus-messaging/src/callerpattern/README.md b/nexus-messaging/src/callerpattern/README.md index b746e7849..af065a07e 100644 --- a/nexus-messaging/src/callerpattern/README.md +++ b/nexus-messaging/src/callerpattern/README.md @@ -16,19 +16,23 @@ The caller workflow: ### Running -Start a Temporal server: +Start a compatible Temporal dev server with Workflow Update callbacks enabled: ```bash -temporal server start-dev +./temporal server start-dev \ + --dynamic-config-value history.enableCHASMCallbacks=true \ + --dynamic-config-value history.enableUpdateCallbacks=true \ + --namespace nexus-messaging-handler-namespace \ + --namespace nexus-messaging-caller-namespace ``` -Create the namespaces and Nexus endpoint: +This sample requires a Temporal dev-server build that supports Workflow Update callbacks. Download the compatible +binary from the [Temporal CLI pre-release instructions](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support). -```bash -temporal operator namespace create --namespace nexus-messaging-handler-namespace -temporal operator namespace create --namespace nexus-messaging-caller-namespace +Create the Nexus endpoint: -temporal operator nexus endpoint create \ +```bash +./temporal operator nexus endpoint create \ --name nexus-messaging-nexus-endpoint \ --target-namespace nexus-messaging-handler-namespace \ --target-task-queue nexus-messaging-handler-task-queue diff --git a/nexus-messaging/src/callerpattern/service/handler.ts b/nexus-messaging/src/callerpattern/service/handler.ts index 6c3846f55..11307e67e 100644 --- a/nexus-messaging/src/callerpattern/service/handler.ts +++ b/nexus-messaging/src/callerpattern/service/handler.ts @@ -26,9 +26,8 @@ export const nexusGreetingServiceHandler = nexus.serviceHandler(nexusGreetingSer setLanguage: new temporalNexus.TemporalOperationHandler({ async start(_ctx, client, input: SetLanguageInput) { - const handle = client.client.workflow.getHandle(workflowIdForUser(input.userId)); - const result = await handle.executeUpdate(setLanguageUpdate, { args: [input.language] }); - return temporalNexus.TemporalOperationResult.sync(result); + const handle = client.getWorkflowHandle(workflowIdForUser(input.userId)); + return await handle.update(setLanguageUpdate, { args: [input.language] }); }, }), diff --git a/nexus-messaging/src/ondemandpattern/README.md b/nexus-messaging/src/ondemandpattern/README.md index 32eadeef5..fe15a38a0 100644 --- a/nexus-messaging/src/ondemandpattern/README.md +++ b/nexus-messaging/src/ondemandpattern/README.md @@ -15,19 +15,23 @@ The caller workflow: ### Running -Start a Temporal server: +Start a compatible Temporal dev server with Workflow Update callbacks enabled: ```bash -temporal server start-dev +./temporal server start-dev \ + --dynamic-config-value history.enableCHASMCallbacks=true \ + --dynamic-config-value history.enableUpdateCallbacks=true \ + --namespace nexus-messaging-handler-namespace \ + --namespace nexus-messaging-caller-namespace ``` -Create the namespaces and Nexus endpoint: +This sample requires a Temporal dev-server build that supports Workflow Update callbacks. Download the compatible +binary from the [Temporal CLI pre-release instructions](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support). -```bash -temporal operator namespace create --namespace nexus-messaging-handler-namespace -temporal operator namespace create --namespace nexus-messaging-caller-namespace +Create the Nexus endpoint: -temporal operator nexus endpoint create \ +```bash +./temporal operator nexus endpoint create \ --name nexus-messaging-nexus-endpoint \ --target-namespace nexus-messaging-handler-namespace \ --target-task-queue nexus-messaging-handler-task-queue diff --git a/nexus-messaging/src/ondemandpattern/service/handler.ts b/nexus-messaging/src/ondemandpattern/service/handler.ts index d2084eb96..54fbbe748 100644 --- a/nexus-messaging/src/ondemandpattern/service/handler.ts +++ b/nexus-messaging/src/ondemandpattern/service/handler.ts @@ -45,9 +45,8 @@ export const nexusRemoteGreetingServiceHandler = nexus.serviceHandler(nexusRemot setLanguage: new temporalNexus.TemporalOperationHandler({ async start(_ctx, client, input: SetLanguageInput) { - const handle = client.client.workflow.getHandle(getWorkflowId(input.userId)); - const result = await handle.executeUpdate(setLanguageUpdate, { args: [input.language] }); - return temporalNexus.TemporalOperationResult.sync(result); + const handle = client.getWorkflowHandle(getWorkflowId(input.userId)); + return await handle.update(setLanguageUpdate, { args: [input.language] }); }, }), From 4f0a5158efee9e7818f2106327cf7e2d2e237de9 Mon Sep 17 00:00:00 2001 From: Joshua Frenchwood Date: Wed, 26 Aug 2026 13:13:56 -0500 Subject: [PATCH 3/3] Adding config value for signal backlinks --- nexus-messaging/src/callerpattern/README.md | 1 + nexus-messaging/src/callerpattern/service/handler.ts | 2 +- nexus-messaging/src/ondemandpattern/README.md | 1 + nexus-messaging/src/ondemandpattern/service/handler.ts | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nexus-messaging/src/callerpattern/README.md b/nexus-messaging/src/callerpattern/README.md index af065a07e..16aae3753 100644 --- a/nexus-messaging/src/callerpattern/README.md +++ b/nexus-messaging/src/callerpattern/README.md @@ -22,6 +22,7 @@ Start a compatible Temporal dev server with Workflow Update callbacks enabled: ./temporal server start-dev \ --dynamic-config-value history.enableCHASMCallbacks=true \ --dynamic-config-value history.enableUpdateCallbacks=true \ + --dynamic-config-value history.enableCHASMSignalBacklinks=true \ --namespace nexus-messaging-handler-namespace \ --namespace nexus-messaging-caller-namespace ``` diff --git a/nexus-messaging/src/callerpattern/service/handler.ts b/nexus-messaging/src/callerpattern/service/handler.ts index 11307e67e..fb28c3976 100644 --- a/nexus-messaging/src/callerpattern/service/handler.ts +++ b/nexus-messaging/src/callerpattern/service/handler.ts @@ -33,7 +33,7 @@ export const nexusGreetingServiceHandler = nexus.serviceHandler(nexusGreetingSer approve: new temporalNexus.TemporalOperationHandler({ async start(_ctx, client, input: ApproveInput) { - const handle = client.client.workflow.getHandle(workflowIdForUser(input.userId)); + const handle = client.getWorkflowHandle(workflowIdForUser(input.userId)); await handle.signal(approveSignal); return temporalNexus.TemporalOperationResult.sync(undefined); }, diff --git a/nexus-messaging/src/ondemandpattern/README.md b/nexus-messaging/src/ondemandpattern/README.md index fe15a38a0..4e35d9e9f 100644 --- a/nexus-messaging/src/ondemandpattern/README.md +++ b/nexus-messaging/src/ondemandpattern/README.md @@ -21,6 +21,7 @@ Start a compatible Temporal dev server with Workflow Update callbacks enabled: ./temporal server start-dev \ --dynamic-config-value history.enableCHASMCallbacks=true \ --dynamic-config-value history.enableUpdateCallbacks=true \ + --dynamic-config-value history.enableCHASMSignalBacklinks=true \ --namespace nexus-messaging-handler-namespace \ --namespace nexus-messaging-caller-namespace ``` diff --git a/nexus-messaging/src/ondemandpattern/service/handler.ts b/nexus-messaging/src/ondemandpattern/service/handler.ts index 54fbbe748..aae3f67c5 100644 --- a/nexus-messaging/src/ondemandpattern/service/handler.ts +++ b/nexus-messaging/src/ondemandpattern/service/handler.ts @@ -52,7 +52,7 @@ export const nexusRemoteGreetingServiceHandler = nexus.serviceHandler(nexusRemot approve: new temporalNexus.TemporalOperationHandler({ async start(_ctx, client, input: ApproveInput) { - const handle = client.client.workflow.getHandle(getWorkflowId(input.userId)); + const handle = client.getWorkflowHandle(getWorkflowId(input.userId)); await handle.signal(approveSignal); return temporalNexus.TemporalOperationResult.sync(undefined); },