Skip to content

Commit 6bebbc2

Browse files
Update Typescript Nexus Details (#4441)
* WIP nexus typescript public preview changes * remove experimental notice * fix broken link * replace a couple more usages of old createNexusClient function --------- Co-authored-by: Jwahir Sundai <jwahir.sundai@temporal.io>
1 parent 8ab96d8 commit 6bebbc2

3 files changed

Lines changed: 100 additions & 6 deletions

File tree

docs/develop/typescript/nexus/feature-guide.mdx

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,14 @@ export const helloServiceHandler = nexus.serviceHandler(helloService, {
190190
A common pattern is to use the Temporal Client from within a sync handler to Signal, Query, or Update a Workflow.
191191
You can also use Signal-With-Start or Update-With-Start to ensure the Workflow is started and send it a Signal or Update.
192192
All calls must complete within the [Nexus request timeout](/cloud/limits#nexus-operation-request-timeout).
193-
The handler receives an AbortSignal via options.signal that is triggered when the deadline is exceeded
193+
The handler receives an AbortSignal via `ctx.abortSignal` that is triggered when the deadline is exceeded
194194
pass it to Temporal Client calls to ensure they are canceled if the timeout is reached.
195195
Updates should be short-lived to stay within this deadline.
196196

197+
The handler context also exposes `ctx.requestDeadline` as an optional `Date`, representing the time by which the current request must complete.
198+
Note that this is the deadline for the current _request_, not the overall operation.
199+
Use it to make decisions about whether to start work that may not finish in time, or to set timeouts on downstream calls.
200+
197201
### Develop an Asynchronous Nexus Operation handler to start a Workflow
198202

199203
Use `@temporalio/nexus`'s `WorkflowRunOperationHandler` helper class to easily expose a Temporal Workflow as a Nexus Operation.
@@ -269,7 +273,7 @@ import { helloServiceHandler } from './handler';
269273

270274
## Develop a caller Workflow that uses the Nexus Service {#develop-caller-workflow-nexus-service}
271275

272-
To execute a Nexus Operation from a Workflow, import the necessary service definition types, then use `@temporalio/workflow`'s `createNexusClient` to create a Nexus client for that service.
276+
To execute a Nexus Operation from a Workflow, import the necessary service definition types, then use `@temporalio/workflow`'s `createNexusServiceClient` to create a Nexus client for that service.
273277
You will need to provide the Nexus Endpoint name, which you registered previously in [Create a Nexus Endpoint to route requests from caller to handler](#create-nexus-endpoint).
274278

275279
<!--SNIPSTART typescript-nexus-hello-service-caller-workflow {"selectedLines": ["1-5","21-34"]}-->
@@ -283,7 +287,7 @@ import { helloService, LanguageCode } from "../service/api";
283287
const HELLO_SERVICE_ENDPOINT = "hello-service-endpoint-name";
284288

285289
export async function helloCallerWorkflow(name: string, language: LanguageCode): Promise<string> {
286-
const nexusClient = wf.createNexusClient({
290+
const nexusClient = wf.createNexusServiceClient({
287291
service: helloService,
288292
endpoint: HELLO_SERVICE_ENDPOINT,
289293
});
@@ -440,6 +444,36 @@ For **synchronous Nexus Operations** the following are reported in the caller's
440444

441445
:::
442446

447+
### OpenTelemetry
448+
449+
The `@temporalio/interceptors-opentelemetry` package supports Nexus Operations, providing automatic trace context propagation across Nexus boundaries from the caller Workflow to the handler.
450+
451+
The easiest way to enable it is with the `OpenTelemetryPlugin`, which auto-registers Nexus interceptors alongside Activity and Workflow interceptors:
452+
453+
```ts
454+
import { OpenTelemetryPlugin } from '@temporalio/interceptors-opentelemetry';
455+
456+
const plugin = new OpenTelemetryPlugin({
457+
resource: myResource,
458+
spanProcessor: mySpanProcessor,
459+
});
460+
461+
const worker = await Worker.create({
462+
// ...
463+
plugins: [plugin],
464+
nexusServices: [myServiceHandler],
465+
});
466+
```
467+
468+
The plugin creates the following spans:
469+
470+
- **Caller side:** `StartNexusOperation:service/operation` — created when the caller Workflow starts a Nexus Operation.
471+
- **Handler side:** `RunStartNexusOperation:service/operation` and `RunCancelNexusOperation:service/operation` — created when the handler processes the operation. These spans are children of the caller span, linked via trace context propagated in Nexus request headers.
472+
473+
See the [interceptors-opentelemetry sample](https://github.com/temporalio/samples-typescript/tree/main/interceptors-opentelemetry) for a complete example.
474+
475+
For custom interceptor logic beyond tracing (e.g., logging, authorization), see [Nexus interceptor registration](/develop/typescript/workers/interceptors#nexus-interceptor-registration).
476+
443477
## Learn more
444478

445479
- Read the high-level description of the [Temporal Nexus feature](/evaluate/nexus) and watch the [Nexus keynote and demo](https://youtu.be/qqc2vsv1mrU?feature=shared&t=2082).

docs/develop/typescript/nexus/quickstart.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ The `nexusServices` parameter registers the handler so it can receive Nexus Oper
166166
<>
167167

168168
<CodeSnippet language="typescript">
169-
{`import { proxyActivities, createNexusClient } from '@temporalio/workflow';
169+
{`import { proxyActivities, createNexusServiceClient } from '@temporalio/workflow';
170170
// Only import the activity types
171171
import type * as activities from './activities';
172172
import { sayHelloService } from './service';
@@ -183,7 +183,7 @@ export async function example(name: string): Promise<string> {
183183
const NEXUS_ENDPOINT = 'my-nexus-endpoint-name';
184184
185185
export async function callerWorkflow(name: string): Promise<string> {
186-
const nexusClient = createNexusClient({
186+
const nexusClient = createNexusServiceClient({
187187
service: sayHelloService,
188188
endpoint: NEXUS_ENDPOINT,
189189
});
@@ -203,7 +203,7 @@ The caller Workflow demonstrates the consumer side of Nexus.
203203
Instead of importing handler code directly, the caller only depends on the Service contract.
204204
This keeps the caller and handler decoupled so they can live in separate Namespaces, repositories, or even teams.
205205

206-
The `wf.createNexusClient()` method creates a client bound to your Nexus Service and Endpoint.
206+
The `wf.createNexusServiceClient()` method creates a client bound to your Nexus Service and Endpoint.
207207
`executeOperation` starts the operation and waits for the result.
208208

209209
</SetupStep>

docs/develop/typescript/workers/interceptors.mdx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ sample.
4444
Intercept workflow-related methods of [`Client`](https://typescript.temporal.io/api/classes/client.Client/) and
4545
[`WorkflowHandle`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle) like starting or signaling a
4646
Workflow.
47+
- [NexusInboundCallsInterceptor](https://typescript.temporal.io/api/interfaces/worker.NexusInboundCallsInterceptor):
48+
Intercept inbound Nexus Operation calls like `startOperation` and `cancelOperation`.
49+
- [NexusOutboundCallsInterceptor](https://typescript.temporal.io/api/interfaces/worker.NexusOutboundCallsInterceptor):
50+
Intercept outbound calls from Nexus Operations, such as enriching log attributes and metric tags.
4751

4852
All interceptor methods are optional—it's up to the implementor to choose which methods to intercept.
4953

@@ -74,6 +78,36 @@ export class ActivityLogInterceptor implements WorkflowOutboundCallsInterceptor
7478
}
7579
```
7680

81+
**Log Nexus Operations**
82+
83+
```ts
84+
import type {
85+
NexusInboundCallsInterceptor,
86+
NexusStartOperationInput,
87+
NexusStartOperationOutput,
88+
Next,
89+
} from '@temporalio/worker';
90+
91+
export class NexusOperationLogInterceptor implements NexusInboundCallsInterceptor {
92+
async startOperation(
93+
input: NexusStartOperationInput,
94+
next: Next<NexusInboundCallsInterceptor, 'startOperation'>
95+
): Promise<NexusStartOperationOutput> {
96+
console.log('Starting Nexus operation', {
97+
service: input.ctx.service,
98+
operation: input.ctx.operation,
99+
});
100+
const output = await next(input);
101+
console.log('Nexus operation started', {
102+
service: input.ctx.service,
103+
operation: input.ctx.operation,
104+
async: output.result.isAsync,
105+
});
106+
return output;
107+
}
108+
}
109+
```
110+
77111
## Register an Interceptor {#register-interceptor}
78112

79113
Registering an interceptor means providing it to the SDK so Temporal can invoke it when matching Client or Worker calls occur. Once registered, it runs in the call path and can observe or modify request and response data.
@@ -125,3 +159,29 @@ const worker = await Worker.create({
125159
},
126160
});
127161
```
162+
163+
### Nexus interceptor registration
164+
165+
Nexus interceptors are registered on Worker creation via
166+
[WorkerOptions](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#interceptors).
167+
Pass an array of factory functions to `interceptors.nexus`.
168+
Each factory receives an [`OperationContext`](https://typescript.temporal.io/api/classes/nexus.OperationContext) and returns an object with optional `inbound` and `outbound` interceptors.
169+
170+
`src/worker/index.ts`
171+
172+
```ts
173+
import { NexusOperationLogInterceptor } from './nexus-interceptors';
174+
175+
const worker = await Worker.create({
176+
// ...
177+
nexusServices: [/* your Nexus services */],
178+
interceptors: {
179+
nexus: [
180+
(_ctx) => ({
181+
inbound: new NexusOperationLogInterceptor(),
182+
}),
183+
],
184+
},
185+
});
186+
```
187+

0 commit comments

Comments
 (0)