CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-temporalio--client

TypeScript SDK Client for communicating with Temporal workflow orchestration systems, providing workflow lifecycle management, connection handling, and durable execution patterns.

Overview
Eval results
Files

interceptors.mddocs/

Interceptors

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.

Capabilities

Workflow Client Interceptors

Interceptors for customizing workflow client operations.

/**
 * Interceptor for workflow client calls
 */
interface WorkflowClientInterceptor {
  /** Intercept workflow start operations */
  start?(
    input: WorkflowStartInput,
    next: WorkflowClientCallsInterceptor['start']
  ): Promise<WorkflowHandle>;

  /** Intercept signal with start operations */
  signalWithStart?(
    input: WorkflowSignalWithStartInput,
    next: WorkflowClientCallsInterceptor['signalWithStart']
  ): Promise<WorkflowHandleWithSignaledRunId>;

  /** Intercept workflow update start operations */
  startUpdate?(
    input: WorkflowStartUpdateInput,
    next: WorkflowClientCallsInterceptor['startUpdate']
  ): Promise<WorkflowStartUpdateOutput>;

  /** Intercept workflow update with start operations */
  startUpdateWithStart?(
    input: WorkflowStartUpdateWithStartInput,
    next: WorkflowClientCallsInterceptor['startUpdateWithStart']
  ): Promise<WorkflowStartUpdateWithStartOutput>;

  /** Intercept signal operations */
  signal?(
    input: WorkflowSignalInput,
    next: WorkflowClientCallsInterceptor['signal']
  ): Promise<void>;

  /** Intercept query operations */
  query?(
    input: WorkflowQueryInput,
    next: WorkflowClientCallsInterceptor['query']
  ): Promise<unknown>;

  /** Intercept workflow termination */
  terminate?(
    input: WorkflowTerminateInput,
    next: WorkflowClientCallsInterceptor['terminate']
  ): Promise<void>;

  /** Intercept workflow cancellation */
  cancel?(
    input: WorkflowCancelInput,
    next: WorkflowClientCallsInterceptor['cancel']
  ): Promise<void>;

  /** Intercept workflow describe operations */
  describe?(
    input: WorkflowDescribeInput,
    next: WorkflowClientCallsInterceptor['describe']
  ): Promise<WorkflowExecutionDescription>;
}

Schedule Client Interceptors

Interceptors for customizing schedule client operations.

/**
 * Interceptor for schedule client calls
 */
interface ScheduleClientInterceptor {
  /** Intercept schedule creation */
  create?(
    input: CreateScheduleInput,
    next: ScheduleClientCallsInterceptor['create']
  ): Promise<CreateScheduleOutput>;
}

Combined Client Interceptors

Interface for providing interceptors for all client types.

/**
 * Combined interceptors for all clients
 */
interface ClientInterceptors {
  /** Workflow client interceptors */
  workflow?: WorkflowClientInterceptor[];
  /** Schedule client interceptors */
  schedule?: ScheduleClientInterceptor[];
}

Workflow Interceptor Input/Output Types

Input and output types for workflow interceptor methods.

/**
 * Input for workflow start interceptor
 */
interface WorkflowStartInput {
  readonly workflowType: string;
  readonly options: WorkflowStartOptions;
  readonly headers: Headers;
}

/**
 * Input for workflow start update interceptor
 */
interface WorkflowStartUpdateInput {
  readonly workflowId: string;
  readonly runId?: string;
  readonly updateName: string;
  readonly args: unknown[];
  readonly options: WorkflowUpdateOptions;
  readonly headers: Headers;
}

/**
 * Output for workflow start update interceptor
 */
interface WorkflowStartUpdateOutput {
  readonly updateHandle: WorkflowUpdateHandle<unknown>;
}

/**
 * Input for workflow start update with start interceptor
 */
interface WorkflowStartUpdateWithStartInput {
  readonly workflowType: string;
  readonly workflowStartOptions: WorkflowStartOptions;
  readonly updateName: string;
  readonly updateArgs: unknown[];
  readonly updateOptions: WorkflowUpdateOptions;
  readonly headers: Headers;
}

/**
 * Output for workflow start update with start interceptor
 */
interface WorkflowStartUpdateWithStartOutput {
  readonly updateHandle: WorkflowUpdateHandle<unknown>;
  readonly workflowHandle: WorkflowHandle<unknown>;
}

/**
 * Input for workflow signal interceptor
 */
interface WorkflowSignalInput {
  readonly workflowId: string;
  readonly runId?: string;
  readonly signalName: string;
  readonly args: unknown[];
  readonly headers: Headers;
}

/**
 * Input for workflow signal with start interceptor
 */
interface WorkflowSignalWithStartInput {
  readonly workflowType: string;
  readonly workflowStartOptions: WorkflowStartOptions;
  readonly signalName: string;
  readonly signalArgs: unknown[];
  readonly headers: Headers;
}

/**
 * Input for workflow query interceptor
 */
interface WorkflowQueryInput {
  readonly workflowId: string;
  readonly runId?: string;
  readonly queryType: string;
  readonly args: unknown[];
  readonly queryRejectCondition?: QueryRejectCondition;
  readonly headers: Headers;
}

/**
 * Input for workflow terminate interceptor
 */
interface WorkflowTerminateInput {
  readonly workflowId: string;
  readonly runId?: string;
  readonly reason?: string;
  readonly headers: Headers;
}

/**
 * Input for workflow cancel interceptor
 */
interface WorkflowCancelInput {
  readonly workflowId: string;
  readonly runId?: string;
  readonly headers: Headers;
}

/**
 * Input for workflow describe interceptor
 */
interface WorkflowDescribeInput {
  readonly workflowId: string;
  readonly runId?: string;
  readonly headers: Headers;
}

Schedule Interceptor Input/Output Types

Input and output types for schedule interceptor methods.

/**
 * Input for create schedule interceptor
 */
interface CreateScheduleInput {
  readonly scheduleId: string;
  readonly schedule: ScheduleOptions;
  readonly headers: Headers;
}

/**
 * Output for create schedule interceptor
 */
interface CreateScheduleOutput {
  readonly scheduleHandle: ScheduleHandle;
}

Usage Examples

Logging Interceptor

import { WorkflowClientInterceptor } from "@temporalio/client";

class LoggingInterceptor implements WorkflowClientInterceptor {
  async start(input, next) {
    console.log(`Starting workflow: ${input.workflowType} (ID: ${input.options.workflowId})`);
    const startTime = Date.now();

    try {
      const handle = await next(input);
      console.log(`Workflow started successfully in ${Date.now() - startTime}ms`);
      return handle;
    } catch (error) {
      console.error(`Failed to start workflow: ${error}`);
      throw error;
    }
  }

  async signal(input, next) {
    console.log(`Sending signal: ${input.signalName} to workflow ${input.workflowId}`);
    return await next(input);
  }

  async query(input, next) {
    console.log(`Querying workflow: ${input.queryType} on ${input.workflowId}`);
    const result = await next(input);
    console.log(`Query result:`, result);
    return result;
  }
}

// Usage
const client = new Client({
  interceptors: {
    workflow: [new LoggingInterceptor()],
  },
});

Metrics Interceptor

interface MetricsCollector {
  increment(metric: string, tags?: Record<string, string>): void;
  timing(metric: string, duration: number, tags?: Record<string, string>): void;
}

class MetricsInterceptor implements WorkflowClientInterceptor {
  constructor(private metrics: MetricsCollector) {}

  async start(input, next) {
    const tags = { workflow_type: input.workflowType };
    const startTime = Date.now();

    try {
      const handle = await next(input);
      this.metrics.increment('workflow.start.success', tags);
      this.metrics.timing('workflow.start.duration', Date.now() - startTime, tags);
      return handle;
    } catch (error) {
      this.metrics.increment('workflow.start.failure', tags);
      throw error;
    }
  }

  async signal(input, next) {
    const tags = { signal_name: input.signalName };
    this.metrics.increment('workflow.signal.sent', tags);
    return await next(input);
  }

  async query(input, next) {
    const tags = { query_type: input.queryType };
    const startTime = Date.now();

    try {
      const result = await next(input);
      this.metrics.increment('workflow.query.success', tags);
      this.metrics.timing('workflow.query.duration', Date.now() - startTime, tags);
      return result;
    } catch (error) {
      this.metrics.increment('workflow.query.failure', tags);
      throw error;
    }
  }
}

Authentication Interceptor

class AuthInterceptor implements WorkflowClientInterceptor {
  constructor(private getAuthToken: () => Promise<string>) {}

  async start(input, next) {
    const token = await this.getAuthToken();
    const enhancedInput = {
      ...input,
      headers: {
        ...input.headers,
        authorization: `Bearer ${token}`,
      },
    };
    return await next(enhancedInput);
  }

  async signal(input, next) {
    const token = await this.getAuthToken();
    const enhancedInput = {
      ...input,
      headers: {
        ...input.headers,
        authorization: `Bearer ${token}`,
      },
    };
    return await next(enhancedInput);
  }

  async query(input, next) {
    const token = await this.getAuthToken();
    const enhancedInput = {
      ...input,
      headers: {
        ...input.headers,
        authorization: `Bearer ${token}`,
      },
    };
    return await next(enhancedInput);
  }
}

Request Transformation Interceptor

class RequestTransformInterceptor implements WorkflowClientInterceptor {
  async start(input, next) {
    // Transform workflow options based on environment
    const transformedOptions = {
      ...input.options,
      taskQueue: this.transformTaskQueue(input.options.taskQueue),
      memo: {
        ...input.options.memo,
        environment: process.env.NODE_ENV,
        version: process.env.APP_VERSION,
      },
    };

    return await next({
      ...input,
      options: transformedOptions,
    });
  }

  private transformTaskQueue(originalQueue: string): string {
    const env = process.env.NODE_ENV || 'development';
    return env === 'production' ? originalQueue : `${env}-${originalQueue}`;
  }
}

Retry Interceptor

class RetryInterceptor implements WorkflowClientInterceptor {
  constructor(
    private maxRetries: number = 3,
    private baseDelay: number = 1000
  ) {}

  async start(input, next) {
    return await this.withRetry(() => next(input));
  }

  async signal(input, next) {
    return await this.withRetry(() => next(input));
  }

  async query(input, next) {
    return await this.withRetry(() => next(input));
  }

  private async withRetry<T>(operation: () => Promise<T>): Promise<T> {
    let lastError: Error;

    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error as Error;

        // Don't retry on client errors
        if (error instanceof WorkflowFailedError) {
          throw error;
        }

        if (attempt < this.maxRetries) {
          const delay = this.baseDelay * Math.pow(2, attempt);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }

        throw error;
      }
    }

    throw lastError!;
  }
}

Combined Interceptor Usage

import { Client } from "@temporalio/client";

// Create multiple interceptors
const loggingInterceptor = new LoggingInterceptor();
const metricsInterceptor = new MetricsInterceptor(metricsCollector);
const authInterceptor = new AuthInterceptor(getAuthToken);
const retryInterceptor = new RetryInterceptor(3, 1000);

// Create client with multiple interceptors
const client = new Client({
  interceptors: {
    workflow: [
      loggingInterceptor,
      metricsInterceptor,
      authInterceptor,
      retryInterceptor,
    ],
    schedule: [], // Add schedule interceptors if needed
  },
});

// Interceptors are applied in order, creating a chain

Schedule Interceptor Example

class ScheduleLoggingInterceptor implements ScheduleClientInterceptor {
  async create(input, next) {
    console.log(`Creating schedule: ${input.scheduleId}`);
    console.log(`Schedule spec:`, input.schedule.spec);

    try {
      const result = await next(input);
      console.log(`Schedule ${input.scheduleId} created successfully`);
      return result;
    } catch (error) {
      console.error(`Failed to create schedule ${input.scheduleId}:`, error);
      throw error;
    }
  }
}

// Usage with schedule interceptor
const client = new Client({
  interceptors: {
    schedule: [new ScheduleLoggingInterceptor()],
  },
});

Install with Tessl CLI

npx tessl i tessl/npm-temporalio--client

docs

activity-completion.md

client-connection.md

error-handling.md

index.md

interceptors.md

schedule-client.md

task-queue-client.md

workflow-client.md

tile.json