0
# Interceptors
1
2
Pluggable system for customizing client behavior through interceptors that can modify requests, responses, and add cross-cutting concerns like logging, metrics, authentication, and request transformation.
3
4
## Capabilities
5
6
### Workflow Client Interceptors
7
8
Interceptors for customizing workflow client operations.
9
10
```typescript { .api }
11
/**
12
* Interceptor for workflow client calls
13
*/
14
interface WorkflowClientInterceptor {
15
/** Intercept workflow start operations */
16
start?(
17
input: WorkflowStartInput,
18
next: WorkflowClientCallsInterceptor['start']
19
): Promise<WorkflowHandle>;
20
21
/** Intercept signal with start operations */
22
signalWithStart?(
23
input: WorkflowSignalWithStartInput,
24
next: WorkflowClientCallsInterceptor['signalWithStart']
25
): Promise<WorkflowHandleWithSignaledRunId>;
26
27
/** Intercept workflow update start operations */
28
startUpdate?(
29
input: WorkflowStartUpdateInput,
30
next: WorkflowClientCallsInterceptor['startUpdate']
31
): Promise<WorkflowStartUpdateOutput>;
32
33
/** Intercept workflow update with start operations */
34
startUpdateWithStart?(
35
input: WorkflowStartUpdateWithStartInput,
36
next: WorkflowClientCallsInterceptor['startUpdateWithStart']
37
): Promise<WorkflowStartUpdateWithStartOutput>;
38
39
/** Intercept signal operations */
40
signal?(
41
input: WorkflowSignalInput,
42
next: WorkflowClientCallsInterceptor['signal']
43
): Promise<void>;
44
45
/** Intercept query operations */
46
query?(
47
input: WorkflowQueryInput,
48
next: WorkflowClientCallsInterceptor['query']
49
): Promise<unknown>;
50
51
/** Intercept workflow termination */
52
terminate?(
53
input: WorkflowTerminateInput,
54
next: WorkflowClientCallsInterceptor['terminate']
55
): Promise<void>;
56
57
/** Intercept workflow cancellation */
58
cancel?(
59
input: WorkflowCancelInput,
60
next: WorkflowClientCallsInterceptor['cancel']
61
): Promise<void>;
62
63
/** Intercept workflow describe operations */
64
describe?(
65
input: WorkflowDescribeInput,
66
next: WorkflowClientCallsInterceptor['describe']
67
): Promise<WorkflowExecutionDescription>;
68
}
69
```
70
71
### Schedule Client Interceptors
72
73
Interceptors for customizing schedule client operations.
74
75
```typescript { .api }
76
/**
77
* Interceptor for schedule client calls
78
*/
79
interface ScheduleClientInterceptor {
80
/** Intercept schedule creation */
81
create?(
82
input: CreateScheduleInput,
83
next: ScheduleClientCallsInterceptor['create']
84
): Promise<CreateScheduleOutput>;
85
}
86
```
87
88
### Combined Client Interceptors
89
90
Interface for providing interceptors for all client types.
91
92
```typescript { .api }
93
/**
94
* Combined interceptors for all clients
95
*/
96
interface ClientInterceptors {
97
/** Workflow client interceptors */
98
workflow?: WorkflowClientInterceptor[];
99
/** Schedule client interceptors */
100
schedule?: ScheduleClientInterceptor[];
101
}
102
```
103
104
### Workflow Interceptor Input/Output Types
105
106
Input and output types for workflow interceptor methods.
107
108
```typescript { .api }
109
/**
110
* Input for workflow start interceptor
111
*/
112
interface WorkflowStartInput {
113
readonly workflowType: string;
114
readonly options: WorkflowStartOptions;
115
readonly headers: Headers;
116
}
117
118
/**
119
* Input for workflow start update interceptor
120
*/
121
interface WorkflowStartUpdateInput {
122
readonly workflowId: string;
123
readonly runId?: string;
124
readonly updateName: string;
125
readonly args: unknown[];
126
readonly options: WorkflowUpdateOptions;
127
readonly headers: Headers;
128
}
129
130
/**
131
* Output for workflow start update interceptor
132
*/
133
interface WorkflowStartUpdateOutput {
134
readonly updateHandle: WorkflowUpdateHandle<unknown>;
135
}
136
137
/**
138
* Input for workflow start update with start interceptor
139
*/
140
interface WorkflowStartUpdateWithStartInput {
141
readonly workflowType: string;
142
readonly workflowStartOptions: WorkflowStartOptions;
143
readonly updateName: string;
144
readonly updateArgs: unknown[];
145
readonly updateOptions: WorkflowUpdateOptions;
146
readonly headers: Headers;
147
}
148
149
/**
150
* Output for workflow start update with start interceptor
151
*/
152
interface WorkflowStartUpdateWithStartOutput {
153
readonly updateHandle: WorkflowUpdateHandle<unknown>;
154
readonly workflowHandle: WorkflowHandle<unknown>;
155
}
156
157
/**
158
* Input for workflow signal interceptor
159
*/
160
interface WorkflowSignalInput {
161
readonly workflowId: string;
162
readonly runId?: string;
163
readonly signalName: string;
164
readonly args: unknown[];
165
readonly headers: Headers;
166
}
167
168
/**
169
* Input for workflow signal with start interceptor
170
*/
171
interface WorkflowSignalWithStartInput {
172
readonly workflowType: string;
173
readonly workflowStartOptions: WorkflowStartOptions;
174
readonly signalName: string;
175
readonly signalArgs: unknown[];
176
readonly headers: Headers;
177
}
178
179
/**
180
* Input for workflow query interceptor
181
*/
182
interface WorkflowQueryInput {
183
readonly workflowId: string;
184
readonly runId?: string;
185
readonly queryType: string;
186
readonly args: unknown[];
187
readonly queryRejectCondition?: QueryRejectCondition;
188
readonly headers: Headers;
189
}
190
191
/**
192
* Input for workflow terminate interceptor
193
*/
194
interface WorkflowTerminateInput {
195
readonly workflowId: string;
196
readonly runId?: string;
197
readonly reason?: string;
198
readonly headers: Headers;
199
}
200
201
/**
202
* Input for workflow cancel interceptor
203
*/
204
interface WorkflowCancelInput {
205
readonly workflowId: string;
206
readonly runId?: string;
207
readonly headers: Headers;
208
}
209
210
/**
211
* Input for workflow describe interceptor
212
*/
213
interface WorkflowDescribeInput {
214
readonly workflowId: string;
215
readonly runId?: string;
216
readonly headers: Headers;
217
}
218
```
219
220
### Schedule Interceptor Input/Output Types
221
222
Input and output types for schedule interceptor methods.
223
224
```typescript { .api }
225
/**
226
* Input for create schedule interceptor
227
*/
228
interface CreateScheduleInput {
229
readonly scheduleId: string;
230
readonly schedule: ScheduleOptions;
231
readonly headers: Headers;
232
}
233
234
/**
235
* Output for create schedule interceptor
236
*/
237
interface CreateScheduleOutput {
238
readonly scheduleHandle: ScheduleHandle;
239
}
240
```
241
242
## Usage Examples
243
244
### Logging Interceptor
245
246
```typescript
247
import { WorkflowClientInterceptor } from "@temporalio/client";
248
249
class LoggingInterceptor implements WorkflowClientInterceptor {
250
async start(input, next) {
251
console.log(`Starting workflow: ${input.workflowType} (ID: ${input.options.workflowId})`);
252
const startTime = Date.now();
253
254
try {
255
const handle = await next(input);
256
console.log(`Workflow started successfully in ${Date.now() - startTime}ms`);
257
return handle;
258
} catch (error) {
259
console.error(`Failed to start workflow: ${error}`);
260
throw error;
261
}
262
}
263
264
async signal(input, next) {
265
console.log(`Sending signal: ${input.signalName} to workflow ${input.workflowId}`);
266
return await next(input);
267
}
268
269
async query(input, next) {
270
console.log(`Querying workflow: ${input.queryType} on ${input.workflowId}`);
271
const result = await next(input);
272
console.log(`Query result:`, result);
273
return result;
274
}
275
}
276
277
// Usage
278
const client = new Client({
279
interceptors: {
280
workflow: [new LoggingInterceptor()],
281
},
282
});
283
```
284
285
### Metrics Interceptor
286
287
```typescript
288
interface MetricsCollector {
289
increment(metric: string, tags?: Record<string, string>): void;
290
timing(metric: string, duration: number, tags?: Record<string, string>): void;
291
}
292
293
class MetricsInterceptor implements WorkflowClientInterceptor {
294
constructor(private metrics: MetricsCollector) {}
295
296
async start(input, next) {
297
const tags = { workflow_type: input.workflowType };
298
const startTime = Date.now();
299
300
try {
301
const handle = await next(input);
302
this.metrics.increment('workflow.start.success', tags);
303
this.metrics.timing('workflow.start.duration', Date.now() - startTime, tags);
304
return handle;
305
} catch (error) {
306
this.metrics.increment('workflow.start.failure', tags);
307
throw error;
308
}
309
}
310
311
async signal(input, next) {
312
const tags = { signal_name: input.signalName };
313
this.metrics.increment('workflow.signal.sent', tags);
314
return await next(input);
315
}
316
317
async query(input, next) {
318
const tags = { query_type: input.queryType };
319
const startTime = Date.now();
320
321
try {
322
const result = await next(input);
323
this.metrics.increment('workflow.query.success', tags);
324
this.metrics.timing('workflow.query.duration', Date.now() - startTime, tags);
325
return result;
326
} catch (error) {
327
this.metrics.increment('workflow.query.failure', tags);
328
throw error;
329
}
330
}
331
}
332
```
333
334
### Authentication Interceptor
335
336
```typescript
337
class AuthInterceptor implements WorkflowClientInterceptor {
338
constructor(private getAuthToken: () => Promise<string>) {}
339
340
async start(input, next) {
341
const token = await this.getAuthToken();
342
const enhancedInput = {
343
...input,
344
headers: {
345
...input.headers,
346
authorization: `Bearer ${token}`,
347
},
348
};
349
return await next(enhancedInput);
350
}
351
352
async signal(input, next) {
353
const token = await this.getAuthToken();
354
const enhancedInput = {
355
...input,
356
headers: {
357
...input.headers,
358
authorization: `Bearer ${token}`,
359
},
360
};
361
return await next(enhancedInput);
362
}
363
364
async query(input, next) {
365
const token = await this.getAuthToken();
366
const enhancedInput = {
367
...input,
368
headers: {
369
...input.headers,
370
authorization: `Bearer ${token}`,
371
},
372
};
373
return await next(enhancedInput);
374
}
375
}
376
```
377
378
### Request Transformation Interceptor
379
380
```typescript
381
class RequestTransformInterceptor implements WorkflowClientInterceptor {
382
async start(input, next) {
383
// Transform workflow options based on environment
384
const transformedOptions = {
385
...input.options,
386
taskQueue: this.transformTaskQueue(input.options.taskQueue),
387
memo: {
388
...input.options.memo,
389
environment: process.env.NODE_ENV,
390
version: process.env.APP_VERSION,
391
},
392
};
393
394
return await next({
395
...input,
396
options: transformedOptions,
397
});
398
}
399
400
private transformTaskQueue(originalQueue: string): string {
401
const env = process.env.NODE_ENV || 'development';
402
return env === 'production' ? originalQueue : `${env}-${originalQueue}`;
403
}
404
}
405
```
406
407
### Retry Interceptor
408
409
```typescript
410
class RetryInterceptor implements WorkflowClientInterceptor {
411
constructor(
412
private maxRetries: number = 3,
413
private baseDelay: number = 1000
414
) {}
415
416
async start(input, next) {
417
return await this.withRetry(() => next(input));
418
}
419
420
async signal(input, next) {
421
return await this.withRetry(() => next(input));
422
}
423
424
async query(input, next) {
425
return await this.withRetry(() => next(input));
426
}
427
428
private async withRetry<T>(operation: () => Promise<T>): Promise<T> {
429
let lastError: Error;
430
431
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
432
try {
433
return await operation();
434
} catch (error) {
435
lastError = error as Error;
436
437
// Don't retry on client errors
438
if (error instanceof WorkflowFailedError) {
439
throw error;
440
}
441
442
if (attempt < this.maxRetries) {
443
const delay = this.baseDelay * Math.pow(2, attempt);
444
await new Promise(resolve => setTimeout(resolve, delay));
445
continue;
446
}
447
448
throw error;
449
}
450
}
451
452
throw lastError!;
453
}
454
}
455
```
456
457
### Combined Interceptor Usage
458
459
```typescript
460
import { Client } from "@temporalio/client";
461
462
// Create multiple interceptors
463
const loggingInterceptor = new LoggingInterceptor();
464
const metricsInterceptor = new MetricsInterceptor(metricsCollector);
465
const authInterceptor = new AuthInterceptor(getAuthToken);
466
const retryInterceptor = new RetryInterceptor(3, 1000);
467
468
// Create client with multiple interceptors
469
const client = new Client({
470
interceptors: {
471
workflow: [
472
loggingInterceptor,
473
metricsInterceptor,
474
authInterceptor,
475
retryInterceptor,
476
],
477
schedule: [], // Add schedule interceptors if needed
478
},
479
});
480
481
// Interceptors are applied in order, creating a chain
482
```
483
484
### Schedule Interceptor Example
485
486
```typescript
487
class ScheduleLoggingInterceptor implements ScheduleClientInterceptor {
488
async create(input, next) {
489
console.log(`Creating schedule: ${input.scheduleId}`);
490
console.log(`Schedule spec:`, input.schedule.spec);
491
492
try {
493
const result = await next(input);
494
console.log(`Schedule ${input.scheduleId} created successfully`);
495
return result;
496
} catch (error) {
497
console.error(`Failed to create schedule ${input.scheduleId}:`, error);
498
throw error;
499
}
500
}
501
}
502
503
// Usage with schedule interceptor
504
const client = new Client({
505
interceptors: {
506
schedule: [new ScheduleLoggingInterceptor()],
507
},
508
});
509
```