From 7ce5ca13291bfc508cfa9c3e578d2bb25b5280b4 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Thu, 13 Nov 2025 12:24:10 -0800 Subject: [PATCH 1/9] docs: add TS to envconfig --- docs/develop/dotnet/benign-exceptions.mdx | 2 +- docs/develop/go/temporal-client.mdx | 5 +- docs/develop/java/benign-exceptions.mdx | 2 +- docs/develop/python/benign-exceptions.mdx | 2 +- docs/develop/ruby/benign-exceptions.mdx | 2 +- docs/develop/typescript/benign-exceptions.mdx | 2 +- docs/develop/typescript/cancellation.mdx | 8 +- .../typescript/converters-and-encryption.mdx | 8 +- docs/develop/typescript/schedules.mdx | 6 +- docs/develop/typescript/temporal-clients.mdx | 584 +++++++++++++----- 10 files changed, 465 insertions(+), 156 deletions(-) diff --git a/docs/develop/dotnet/benign-exceptions.mdx b/docs/develop/dotnet/benign-exceptions.mdx index 0d3c572b0d..4e741ad524 100644 --- a/docs/develop/dotnet/benign-exceptions.mdx +++ b/docs/develop/dotnet/benign-exceptions.mdx @@ -56,4 +56,4 @@ public class MyActivities } ``` -Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. \ No newline at end of file +Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. diff --git a/docs/develop/go/temporal-client.mdx b/docs/develop/go/temporal-client.mdx index 2e429402db..9b030287bf 100644 --- a/docs/develop/go/temporal-client.mdx +++ b/docs/develop/go/temporal-client.mdx @@ -206,9 +206,7 @@ code. This is convenient for local development and testing. You can also load a variables or a configuration file, and then override specific options in code. {/* SNIPSTART samples-apps-go-yourapp-gateway {"selectedLines": ["1-23", "32"]} */} - [sample-apps/go/yourapp/gateway/main.go](https://github.com/temporalio/documentation/blob/main/sample-apps/go/yourapp/gateway/main.go) - ```go package main @@ -233,10 +231,9 @@ func main() { log.Fatalln("Unable to create Temporal Client", err) } defer temporalClient.Close() - // ... +// ... } ``` - {/* SNIPEND */} diff --git a/docs/develop/java/benign-exceptions.mdx b/docs/develop/java/benign-exceptions.mdx index b94f26b15e..db785cf6ee 100644 --- a/docs/develop/java/benign-exceptions.mdx +++ b/docs/develop/java/benign-exceptions.mdx @@ -60,4 +60,4 @@ public class MyActivitiesImpl implements MyActivities { } ``` -Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. \ No newline at end of file +Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. diff --git a/docs/develop/python/benign-exceptions.mdx b/docs/develop/python/benign-exceptions.mdx index 3e4de50d9e..5379ae1527 100644 --- a/docs/develop/python/benign-exceptions.mdx +++ b/docs/develop/python/benign-exceptions.mdx @@ -46,4 +46,4 @@ async def my_activity() -> str: ) ``` -Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. \ No newline at end of file +Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. diff --git a/docs/develop/ruby/benign-exceptions.mdx b/docs/develop/ruby/benign-exceptions.mdx index 5b6663d61f..90cf2c4e7a 100644 --- a/docs/develop/ruby/benign-exceptions.mdx +++ b/docs/develop/ruby/benign-exceptions.mdx @@ -48,4 +48,4 @@ class MyActivity < Temporalio::Activity::Definition end ``` -Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. \ No newline at end of file +Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. diff --git a/docs/develop/typescript/benign-exceptions.mdx b/docs/develop/typescript/benign-exceptions.mdx index 1308cce24b..01a485c5a5 100644 --- a/docs/develop/typescript/benign-exceptions.mdx +++ b/docs/develop/typescript/benign-exceptions.mdx @@ -50,4 +50,4 @@ export async function myActivity(): Promise { } ``` -Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. \ No newline at end of file +Use benign exceptions for Activity errors that occur regularly as part of normal operations, such as polling an external service that isn't ready yet, or handling expected transient failures that will be retried. diff --git a/docs/develop/typescript/cancellation.mdx b/docs/develop/typescript/cancellation.mdx index 55c6b52599..c6af590ad8 100644 --- a/docs/develop/typescript/cancellation.mdx +++ b/docs/develop/typescript/cancellation.mdx @@ -69,7 +69,7 @@ To simplify checking for cancellation, use the [packages/test/src/workflows/cancel-timer-immediately.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/cancel-timer-immediately.ts) ```ts -import { CancellationScope, CancelledFailure, sleep } from '@temporalio/workflow'; +import { CancelledFailure, CancellationScope, sleep } from '@temporalio/workflow'; export async function cancelTimer(): Promise { // Timers and Activities are automatically cancelled when their containing scope is cancelled. @@ -95,7 +95,7 @@ Alternatively, the preceding can be written as the following. [packages/test/src/workflows/cancel-timer-immediately-alternative-impl.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/cancel-timer-immediately-alternative-impl.ts) ```ts -import { CancellationScope, CancelledFailure, sleep } from '@temporalio/workflow'; +import { CancelledFailure, CancellationScope, sleep } from '@temporalio/workflow'; export async function cancelTimerAltImpl(): Promise { try { @@ -123,7 +123,7 @@ The following code shows how to handle Workflow cancellation by an external clie [packages/test/src/workflows/handle-external-workflow-cancellation-while-activity-running.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/handle-external-workflow-cancellation-while-activity-running.ts) ```ts -import { CancellationScope, isCancellation, proxyActivities } from '@temporalio/workflow'; +import { CancellationScope, proxyActivities, isCancellation } from '@temporalio/workflow'; import type * as activities from '../activities'; const { httpPostJSON, cleanup } = proxyActivities({ @@ -248,7 +248,7 @@ You can achieve complex flows by nesting cancellation scopes. [packages/test/src/workflows/nested-cancellation.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/nested-cancellation.ts) ```ts -import { CancellationScope, isCancellation, proxyActivities } from '@temporalio/workflow'; +import { CancellationScope, proxyActivities, isCancellation } from '@temporalio/workflow'; import type * as activities from '../activities'; diff --git a/docs/develop/typescript/converters-and-encryption.mdx b/docs/develop/typescript/converters-and-encryption.mdx index 21ebde0ea1..73f363d4a6 100644 --- a/docs/develop/typescript/converters-and-encryption.mdx +++ b/docs/develop/typescript/converters-and-encryption.mdx @@ -261,6 +261,7 @@ const worker = await Worker.create({ [ejson/src/client.ts](https://github.com/temporalio/samples-typescript/blob/main/ejson/src/client.ts) ```ts const client = new Client({ + connection, dataConverter: { payloadConverterPath: require.resolve('./payload-converter') }, }); ``` @@ -398,13 +399,17 @@ const worker = await Worker.create({ [protobufs/src/client.ts](https://github.com/temporalio/samples-typescript/blob/main/protobufs/src/client.ts) ```ts -import { Client } from '@temporalio/client'; +import { Client, Connection } from '@temporalio/client'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; import { v4 as uuid } from 'uuid'; import { foo, ProtoResult } from '../protos/root'; import { example } from './workflows'; async function run() { + const config = loadClientConnectConfig(); + const connection = await Connection.connect(config.connectionOptions); const client = new Client({ + connection, dataConverter: { payloadConverterPath: require.resolve('./payload-converter') }, }); @@ -558,6 +563,7 @@ As before, we provide a custom Data Converter to the Client and Worker: [encryption/src/client.ts](https://github.com/temporalio/samples-typescript/blob/main/encryption/src/client.ts) ```ts const client = new Client({ + connection, dataConverter: await getDataConverter(), }); diff --git a/docs/develop/typescript/schedules.mdx b/docs/develop/typescript/schedules.mdx index fcc47b1b56..c21ab1f9e1 100644 --- a/docs/develop/typescript/schedules.mdx +++ b/docs/develop/typescript/schedules.mdx @@ -49,9 +49,9 @@ The Temporal Service doesn't guarantee when this removal will happen. [schedules/src/start-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/start-schedule.ts) ```ts async function run() { - const client = new Client({ - connection: await Connection.connect(), - }); + const config = loadClientConnectConfig(); + const connection = await Connection.connect(config.connectionOptions); + const client = new Client({ connection }); // https://typescript.temporal.io/api/classes/client.ScheduleClient#create const schedule = await client.schedule.create({ diff --git a/docs/develop/typescript/temporal-clients.mdx b/docs/develop/typescript/temporal-clients.mdx index 59a075e4ff..e94e5bbf22 100644 --- a/docs/develop/typescript/temporal-clients.mdx +++ b/docs/develop/typescript/temporal-clients.mdx @@ -3,7 +3,9 @@ id: temporal-client title: Temporal Client - Typescript SDK sidebar_label: Temporal Client toc_max_heading_level: 4 -description: The Temporal Client SDK enables seamless communication with the Temporal Service, allowing applications to start Workflow Executions, send Signals, and query Workflows efficiently. +description: + The Temporal Client SDK enables seamless communication with the Temporal Service, allowing applications to start + Workflow Executions, send Signals, and query Workflows efficiently. keywords: - temporal typescript client - connect typescript client to temporal service @@ -26,183 +28,469 @@ tags: - Certificates --- -The Temporal Client, provided by the Temporal SDK, allows you to communicate with the Temporal Service. -It acts as the bridge for communication between your applications and the Temporal Service. +A [Temporal Client](https://chatgpt.com/encyclopedia/temporal-sdks#temporal-client) enables you to communicate with the +Temporal Service. Communication with a Temporal Service lets you perform actions such as starting Workflow Executions, +sending Signals and Queries to Workflow Executions, getting Workflow results, and more. You cannot initialize a Temporal +Client inside a Workflow. However, they're commonly initialized inside an Activity to communicate with a Temporal +Service. -The following page shows how to: +This page shows you how to do the following using the TypeScript SDK with the Temporal Client: - [Connect to a local development Temporal Service](#connect-to-development-service) - [Connect to Temporal Cloud](#connect-to-temporal-cloud) -- [Start a Workflow Execution](#start-workflow-execution) +- [Connect to Temporal Service from a Worker](#connect-to-temporal-service-from-a-worker) +- [Start a Workflow Execution](#start-workflow) +- [Get Workflow results](#get-workflow-results) + +In the TypeScript SDK, connecting to Temporal Service from a Temporal Application and from within an Activity rely on a +different type of connection than connecting from a Worker. The sections +[Connect to a local development Temporal Service](#connect-to-development-service) and +[Connect to Temporal Cloud](#connect-to-temporal-cloud) apply to connecting from a Temporal Application or from within +an Activity. See [Connect to Temporal Service from a Worker](#connect-to-temporal-service-from-a-worker) for details on +connecting from a Worker. ## Connect to development Temporal Service {#connect-to-development-service} -**How to connect to the local Temporal CLI development Temporal Service using the Typescript SDK** +To connect to a development Temporal service from a Temporal Application or from within an Activity, use +[`Connect.connect`](https://typescript.temporal.io/api/classes/client.Connection#connect) to create a Connection object +to connect to the Temporal Service, and pass in that connection when you create a new `Client` instance. You can provide +connections directly in code, load them from **environment variables**, or a **TOML configuration file** using the +`@temporalio/envconfig` helpers. We recommend environment variables or a configuration file for secure, repeatable +configuration. + +When you’re running a Temporal Service locally, such as with the +[Temporal CLI dev server](https://docs.temporal.io/cli/server#start-dev), the required options are minimal. If you don't +specify a host/port, the SDK defaults to `127.0.0.1:7233` and the `default` Namespace. -A [Temporal Client](/encyclopedia/temporal-sdks#temporal-client) facilitates communication with the [Temporal Service](/temporal-service), allowing you to perform a variety of actions, such as: + -- Starting Workflow Executions. -- Sending Signals to Workflow Executions. -- Sending Queries to Workflow Executions. -- Getting the results of a Workflow Execution. -- Providing an Activity Task Token. + -:::caution +You can use a TOML configuration file to set connection options for the Temporal Client. The configuration file lets you +configure multiple profiles, each with its own set of connection options. You can then specify which profile to use when +creating the Temporal Client. You can use the environment variable `TEMPORAL_CONFIG_FILE` to specify the location of the +TOML file or provide the path to the file directly in code. If you don't provide the configuration file path, the SDK +looks for it at the path `~/.config/temporalio/temporal.toml` or the equivalent on your OS. Refer to +[Environment Configuration](../environment-configuration.mdx#configuration-methods) for more details about configuration +files and profiles. -A Temporal Client cannot be initialized and used inside a Workflow. -However, it is acceptable and common to use a Temporal Client inside an Activity to communicate with a Temporal Service. +:::info + +The connection options set in configuration files have lower precedence than environment variables. This means that if +you set the same option in both the configuration file and as an environment variable, the environment variable value +overrides the option set in the configuration file. ::: -Running a Temporal Service locally, like through the [Temporal CLI](https://docs.temporal.io/cli/server#start-dev), requires minimal connection configurations. -Many of the SDKs automatically use the default local address and port (127.0.0.1:7233) that Temporalite and [Docker Compose](https://github.com/temporalio/docker-compose) use. +For example, the following TOML configuration file defines two profiles: `default` and `prod`. Each profile has its own +set of connection options. + +```toml title="config.toml" +# Default profile for local development +[profile.default] +address = "localhost:7233" +namespace = "default" + +# Optional: Add custom gRPC headers +[profile.default.grpc_meta] +my-custom-header = "development-value" +trace-id = "dev-trace-123" + +# Production profile for Temporal Cloud +[profile.prod] +address = "your-namespace.a1b2c.tmprl.cloud:7233" +namespace = "your-namespace" +api_key = "your-api-key-here" + +# TLS configuration for production +[profile.prod.tls] +# TLS auto-enables when TLS config or an API key is present +# disabled = false +client_cert_path = "/etc/temporal/certs/client.pem" +client_key_path = "/etc/temporal/certs/client.key" + +# Custom headers for production +[profile.prod.grpc_meta] +environment = "production" +service-version = "v1.2.3" +``` -```ts -import { Client } from '@temporalio/client'; +You can create a Temporal Client using a profile from the configuration file as follows. In this example, you load the +`default` profile for local development: -async function run() { - const client = new Client(); +{/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "{17-19}"} */} +[env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) - // . . . +```ts +import { Connection, Client } from '@temporalio/client'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; +import { resolve } from 'path'; + +async function main() { + console.log('--- Loading default profile from config.toml ---'); + + // For this sample to be self-contained, we explicitly provide the path to + // the config.toml file included in this directory. + // By default though, the config.toml file will be loaded from + // ~/.config/temporalio/temporal.toml (or the equivalent standard config directory on your OS). + const configFile = resolve(__dirname, '../config.toml'); + + // loadClientConnectConfig is a helper that loads a profile and prepares + // the configuration for Connection.connect and Client. By default, it loads the + // "default" profile. + const config = loadClientConnectConfig({ + configSource: { path: configFile }, + }); - await client.connection.close(); + console.log(`Loaded 'default' profile from ${configFile}.`); + console.log(` Address: ${config.connectionOptions.address}`); + console.log(` Namespace: ${config.namespace}`); + console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); + + console.log('\nAttempting to connect to client...'); + try { + const connection = await Connection.connect(config.connectionOptions); + const client = new Client({ connection, namespace: config.namespace }); + console.log('✅ Client connected successfully!'); + await connection.close(); + } catch (err) { + console.log(`❌ Failed to connect: ${err}`); + } } -run().catch((err) => { +main().catch((err) => { console.error(err); process.exit(1); }); ``` -To connect to the Temporal Service, you create a [Connection](https://typescript.temporal.io/api/classes/client.Connection). You then use this Connection when setting up the Client. You can pass the `Connection` instance when creating the [Client](https://typescript.temporal.io/api/classes/client.Client#connection). +{/* SNIPEND */} -If you omit the `Connection` and just create a `new Client()`, it will connect to `localhost:7233`. + -## Connect to Temporal Cloud {#connect-to-temporal-cloud} + -### How to connect to Temporal Cloud using an API key {#connect-to-temporal-cloud-api-key} +Use the `EnvConfig` package to set connection options for the Temporal Client using environment variables. For a list of +all available environment variables and their default values, refer to +[Environment Configuration](/references/client-environment-configuration). -To use an [API key](/cloud/api-keys) with the Temporal TypeScript SDK, you will need to provide additional connection options: +For example, the following code snippet loads all environment variables and creates a Temporal Client with the options +specified in those variables. If you have defined a configuration file at either the default location +(`~/.config/temporalio/temporal.toml`) or a custom location specified by the `TEMPORAL_CONFIG_FILE` environment +variable, this will also load the default profile in the configuration file. However, any options set via environment +variables will take precedence. -- Your _API Key_ value -- Your _Namespace and Account id_ combination, which follows the format `.`. -- The _endpoint_ may vary. The most common endpoint used is the gRPC regional endpoint, which follows the format: `..api.temporal.io:7233`. -- For Namespaces with High Availability features with API key authentication enabled, use the gRPC Namespace endpoint: `..tmprl.cloud:7233`. - This allows automated failover without needing to switch endpoints. +Set the following environment variables before running your .NET application. Replace the placeholder values with your +actual configuration. Since this is for a local development Temporal Service, the values connect to `localhost:7233` and +the `default` Namespace. You may omit these variables entirely since they're the defaults. -You can find the Namespace and Account ID, as well as the endpoint, on the Namespaces tab: +```bash +export TEMPORAL_NAMESPACE="default" +export TEMPORAL_ADDRESS="localhost:7233" +``` -![The Namespace and Account ID combination on the left, and the regional endpoint on the right](/img/cloud/apikeys/namespaces-and-regional-endpoints.png) +After setting the environment variables, use the following code to create the Temporal Client: + +```csharp +using Temporalio.Client; +using Temporalio.Client.EnvConfig; + +namespace TemporalioSamples.EnvConfig; + +/// +/// Sample demonstrating loading the default environment configuration profile +/// from a TOML file. +/// +public static class LoadFromFile +{ + public static async Task RunAsync() + { + try + { + var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); + + Console.WriteLine("\nAttempting to connect to client..."); + + var client = await TemporalClient.ConnectAsync(connectOptions); + Console.WriteLine("✅ Client connected successfully!"); + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + Console.WriteLine($"❌ Failed to connect: {ex.Message}"); + } + } +} +``` -Now, when instantiating a Temporal `connection` in your Temporal TypeScript SDK code, provide the API key with the following `connect` code: + -```typescript -const connection = await Connection.connect({ - address: , - tls: true, - apiKey: , -}); -const client = new Client({ - connection, - namespace: ., -}); -``` + -To create an initial Worker `NativeConnection` (for use with `Worker`): +If you don't want to use environment variables or a configuration file, you can specify connection options directly in +code. This is convenient for local development and testing. You can also load a base configuration from environment +variables or a configuration file, and then override specific options in code. -```typescript -const connection = await NativeConnection.connect({ - address: , - tls: true, - apiKey: , -}); -const worker = await Worker.create({ - connection, - namespace: ., - // ... -}); -``` +```csharp +using System; +using System.Threading.Tasks; +using Temporalio.Client; -To update the API key on an existing `Connection` or `NativeConnection`, use `setApiKey`: +namespace TemporalioSamples.Manual +{ + public static class ManualConnect + { + public static async Task RunAsync() + { + Console.WriteLine("--- Connecting manually to Temporal ---"); + + var client = await TemporalClient.ConnectAsync(new TemporalClientConnectOptions + { + TargetHost = "localhost:7233", + Namespace = "default", + }); + + Console.WriteLine("✅ Connected to local Temporal service!"); + } + } +} -```typescript -connection.setApiKey(); ``` -### How to connect to Temporal Cloud using mTLS {#connect-to-temporal-cloud-tls} + -When you connect to [Temporal Cloud](/cloud) with mTLS, you need to provide additional connection and client options that include the following: + -- The [Temporal Cloud Namespace Id](/cloud/namespaces#temporal-cloud-namespace-id). -- The [Namespace's gRPC endpoint](/cloud/namespaces#temporal-cloud-grpc-endpoint). - An endpoint listing is available at the [Temporal Cloud Website](https://cloud.temporal.io/namespaces) on each Namespace detail page. - The endpoint contains the Namespace Id and port. -- mTLS CA certificate. -- mTLS private key. +## Connect to Temporal Cloud {#connect-to-temporal-cloud} -For more information about managing and generating client certificates for Temporal Cloud, see [How to manage certificates in Temporal Cloud](/cloud/certificates). +You can connect to Temporal Cloud using either an [API key](/cloud/api-keys) or through mTLS. Connection to Temporal +Cloud or any secured Temporal Service requires additional connection options compared to connecting to an unsecured +local development instance: -For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Service, see [Temporal Customization Samples](https://github.com/temporalio/samples-server). +- Your credentials for authentication. + - If you are using an API key, provide the API key value. + - If you are using mTLS, provide the mTLS CA certificate and mTLS private key. +- Your _Namespace and Account ID_ combination, which follows the format `.`. +- The _endpoint_ may vary. The most common endpoint used is the gRPC regional endpoint, which follows the format: + `..api.temporal.io:7233`. +- For Namespaces with High Availability features with API key authentication enabled, use the gRPC Namespace endpoint: + `..tmprl.cloud:7233`. This allows automated failover without needing to switch endpoints. -Create a [`Connection`](https://typescript.temporal.io/api/classes/client.Connection) with a [`connectionOptions`](https://typescript.temporal.io/api/interfaces/client.ConnectionOptions) object that has your Cloud namespace and client certificate. +You can find the Namespace and Account ID, as well as the endpoint, on the Namespaces tab: -```ts -import { Client, Connection } from '@temporalio/client'; -import fs from 'fs-extra'; +![The Namespace and Account ID combination on the left, and the regional endpoint on the right](/img/cloud/apikeys/namespaces-and-regional-endpoints.png) -const { NODE_ENV = 'development' } = process.env; -const isDeployed = ['production', 'staging'].includes(NODE_ENV); +You can provide these connection options using environment variables, a configuration file, or directly in code. -async function run() { - const cert = await fs.readFile('./path-to/your.pem'); - const key = await fs.readFile('./path-to/your.key'); - - let connectionOptions = {}; - if (isDeployed) { - connectionOptions = { - address: 'your-namespace.tmprl.cloud:7233', - tls: { - clientCertPair: { - crt: cert, - key, - }, - }, - }; - - const connection = await Connection.connect(connectionOptions); - - const client = new Client({ - connection, - namespace: 'your-namespace', - }); + - // . . . + - await client.connection.close(); - } +You can use a TOML configuration file to set connection options for the Temporal Client. The configuration file lets you +configure multiple profiles, each with its own set of connection options. You can then specify which profile to use when +creating the Temporal Client. For a list of all available configuration options you can set in the TOML file, refer to +[Environment Configuration](/references/client-environment-configuration). + +You can use the environment variable `TEMPORAL_CONFIG_FILE` to specify the location of the TOML file or provide the path +to the file directly in code. If you don't provide the path to the configuration file, the SDK looks for it at the +default path `~/.config/temporalio/temporal.toml`. + +:::info + +The connection options set in configuration files have lower precedence than environment variables. This means that if +you set the same option in both the configuration file and as an environment variable, the environment variable value +overrides the option set in the configuration file. + +::: + +For example, the following TOML configuration file defines a `cloud` profile with the necessary connection options to +connect to Temporal Cloud via an API key: + +```toml +# Cloud profile for Temporal Cloud +[profile.cloud] +address = "your-namespace.a1b2c.tmprl.cloud:7233" +namespace = "your-namespace" +api_key = "your-api-key-here" +``` + +If you want to use mTLS authentication instead of an API key, replace the `api_key` field with your mTLS certificate and +private key: + +```toml +# Cloud profile for Temporal Cloud +[profile.cloud] +address = "your-namespace.a1b2c.tmprl.cloud:7233" +namespace = "your-namespace" +tls_client_cert_data = "your-tls-client-cert-data" +tls_client_key_path = "your-tls-client-key-path" +``` + +With the connections options defined in the configuration file, use the `ClientEnvConfig.LoadClientConnectOptions` +method to create a Temporal Client using the `staging` profile as follows. After loading the profile, you can also +programmatically override specific connection options before creating the client. + +```csharp title="LoadProfile.cs" {25. 41} +using Temporalio.Client; +using Temporalio.Client.EnvConfig; + +namespace TemporalioSamples.EnvConfig; + +/// +/// Sample demonstrating loading a named environment configuration profile and +/// programmatically overriding its values. +/// +public static class LoadProfile +{ + public static async Task RunAsync() + { + Console.WriteLine("--- Loading 'staging' profile with programmatic overrides ---"); + + try + { + var configFile = Path.Combine(Directory.GetCurrentDirectory(), "config.toml"); + var profileName = "staging"; + + Console.WriteLine("The 'staging' profile in config.toml has an incorrect address (localhost:9999)."); + Console.WriteLine("We'll programmatically override it to the correct address."); + + // Load the 'staging' profile + var connectOptions = ClientEnvConfig.LoadClientConnectOptions(new ClientEnvConfig.ProfileLoadOptions + { + Profile = profileName, + ConfigSource = DataSource.FromPath(configFile), + }); + + // Override the target host to the correct address. + // This is the recommended way to override configuration values. + connectOptions.TargetHost = "localhost:7233"; + + Console.WriteLine($"\nLoaded '{profileName}' profile from {configFile} with overrides."); + Console.WriteLine($" Address: {connectOptions.TargetHost} (overridden from localhost:9999)"); + Console.WriteLine($" Namespace: {connectOptions.Namespace}"); + + Console.WriteLine("\nAttempting to connect to client..."); + + var client = await TemporalClient.ConnectAsync(connectOptions); + Console.WriteLine("✅ Client connected successfully!"); + + // Test the connection by checking the service + var sysInfo = await client.Connection.WorkflowService.GetSystemInfoAsync(new()); + Console.WriteLine("✅ Successfully verified connection to Temporal server!\n{0}", sysInfo); + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + Console.WriteLine($"❌ Failed to connect: {ex.Message}"); + } + } } +``` -run().catch((err) => { - console.error(err); - process.exit(1); + + + + +The following environment variables are required to connect to Temporal Cloud: + +- `TEMPORAL_NAMESPACE`: Your Namespace and Account ID combination in the format `.`. +- `TEMPORAL_ADDRESS`: The gRPC endpoint for your Temporal Cloud Namespace. +- `TEMPORAL_API_KEY`: Your API key value. Required if you are using API key authentication. +- `TEMPORAL_TLS_CLIENT_CERT_DATA` or `TEMPORAL_TLS_CLIENT_CERT_PATH`: Your mTLS client certificate data or file path. + Required if you are using mTLS authentication. +- `TEMPORAL_TLS_CLIENT_KEY_DATA` or `TEMPORAL_TLS_CLIENT_KEY_PATH`: Your mTLS client private key data or file path. + Required if you are using mTLS authentication. + +Ensure these environment variables exist in your environment before running your .NET application. + +Import the `EnvConfig` package to set connection options for the Temporal Client using environment variables. The +`MustLoadDefaultClientOptions` function will automatically load all environment variables. For a list of all available +environment variables and their default values, refer to +[Environment Configuration](/references/client-environment-configuration). + +For example, the following code snippet loads all environment variables and creates a Temporal Client with the options +specified in those variables. If you have defined a configuration file at either the default location +(`~/.config/temporalio/temporal.toml`) or a custom location specified by the `TEMPORAL_CONFIG_FILE` environment +variable, this will also load the default profile in the configuration file. However, any options set via environment +variables will take precedence. + +```csharp {16,20} +using Temporalio.Client; +using Temporalio.Client.EnvConfig; + +namespace TemporalioSamples.EnvConfig; + +/// +/// Sample demonstrating loading the default environment configuration profile +/// from a TOML file. +/// +public static class LoadFromFile +{ + public static async Task RunAsync() + { + try + { + var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); + + Console.WriteLine("\nAttempting to connect to client..."); + + var client = await TemporalClient.ConnectAsync(connectOptions); + Console.WriteLine("✅ Client connected successfully!"); + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + Console.WriteLine($"❌ Failed to connect: {ex.Message}"); + } + } +} +``` + + + + + +You can also provide connections options in your Go code directly. To create an initial connection, provide the +Namespace and API key values to the ` TemporalClient.ConnectAsync` method. + +```csharp +var myClient = TemporalClient.ConnectAsync(new() +{ + Namespace = ".", + ApiKey = "", + Tls = new(), }); ``` +To update an API key, update the value of `ApiKey` on the existing client connection: + +```csharp +myClient.Connection.ApiKey = myKeyUpdated; +``` + + + + + ## Start Workflow Execution {#start-workflow-execution} **How to start a Workflow Execution using the Typescript SDK** -[Workflow Execution](/workflow-execution) semantics rely on several parameters—that is, to start a Workflow Execution you must supply a Task Queue that will be used for the Tasks (one that a Worker is polling), the Workflow Type, language-specific contextual data, and Workflow Function parameters. +[Workflow Execution](/workflow-execution) semantics rely on several parameters—that is, to start a Workflow Execution +you must supply a Task Queue that will be used for the Tasks (one that a Worker is polling), the Workflow Type, +language-specific contextual data, and Workflow Function parameters. -In the examples below, all Workflow Executions are started using a Temporal Client. -To spawn Workflow Executions from within another Workflow Execution, use either the Child Workflow or External Workflow APIs. +In the examples below, all Workflow Executions are started using a Temporal Client. To spawn Workflow Executions from +within another Workflow Execution, use either the Child Workflow or External Workflow APIs. -See the [Customize Workflow Type](/develop/typescript/core-application#workflow-type) section to see how to customize the name of the Workflow Type. +See the [Customize Workflow Type](/develop/typescript/core-application#workflow-type) section to see how to customize +the name of the Workflow Type. -A request to spawn a Workflow Execution causes the Temporal Service to create the first Event ([WorkflowExecutionStarted](/references/events#workflowexecutionstarted)) in the Workflow Execution Event History. -The Temporal Service then creates the first Workflow Task, resulting in the first [WorkflowTaskScheduled](/references/events#workflowtaskscheduled) Event. +A request to spawn a Workflow Execution causes the Temporal Service to create the first Event +([WorkflowExecutionStarted](/references/events#workflowexecutionstarted)) in the Workflow Execution Event History. The +Temporal Service then creates the first Workflow Task, resulting in the first +[WorkflowTaskScheduled](/references/events#workflowtaskscheduled) Event. -When you have a Client, you can schedule the start of a Workflow with `client.workflow.start()`, specifying `workflowId`, `taskQueue`, and `args` and returning a Workflow handle immediately after the Server acknowledges the receipt. +When you have a Client, you can schedule the start of a Workflow with `client.workflow.start()`, specifying +`workflowId`, `taskQueue`, and `args` and returning a Workflow handle immediately after the Server acknowledges the +receipt. ```typescript const handle = await client.workflow.start(example, { @@ -214,33 +502,39 @@ const handle = client.getHandle(workflowId); const result = await handle.result(); ``` -Calling `client.workflow.start()` and `client.workflow.execute()` send a command to Temporal Server to schedule a new Workflow Execution on the specified Task Queue. It does not actually start until a Worker that has a matching Workflow Type, polling that Task Queue, picks it up. +Calling `client.workflow.start()` and `client.workflow.execute()` send a command to Temporal Server to schedule a new +Workflow Execution on the specified Task Queue. It does not actually start until a Worker that has a matching Workflow +Type, polling that Task Queue, picks it up. -You can test this by executing a Client command without a matching Worker. Temporal Server records the command in Event History, but does not make progress with the Workflow Execution until a Worker starts polling with a matching Task Queue and Workflow Definition. +You can test this by executing a Client command without a matching Worker. Temporal Server records the command in Event +History, but does not make progress with the Workflow Execution until a Worker starts polling with a matching Task Queue +and Workflow Definition. -Workflow Execution run in a separate V8 isolate context in order to provide a [deterministic runtime](/workflow-definition#deterministic-constraints). +Workflow Execution run in a separate V8 isolate context in order to provide a +[deterministic runtime](/workflow-definition#deterministic-constraints). ### Set a Workflow's Task Queue {#set-task-queue} In most SDKs, the only Workflow Option that must be set is the name of the [Task Queue](/task-queue). -For any code to execute, a Worker Process must be running that contains a Worker Entity that is polling the same Task Queue name. +For any code to execute, a Worker Process must be running that contains a Worker Entity that is polling the same Task +Queue name. A Task Queue is a dynamic queue in Temporal polled by one or more Workers. -Workers bundle Workflow code and node modules using Webpack v5 and execute them inside V8 isolates. -Activities are directly required and run by Workers in the Node.js environment. +Workers bundle Workflow code and node modules using Webpack v5 and execute them inside V8 isolates. Activities are +directly required and run by Workers in the Node.js environment. -Workers are flexible. -You can host any or all of your Workflows and Activities on a Worker, and you can host multiple Workers on a single machine. +Workers are flexible. You can host any or all of your Workflows and Activities on a Worker, and you can host multiple +Workers on a single machine. The Worker need three main things: - `taskQueue`: The Task Queue to poll. This is the only required argument. - `activities`: Optional. Imported and supplied directly to the Worker. - Workflow bundle. Choose one of the following options: - - Specify `workflowsPath` pointing to your `workflows.ts` file to pass to Webpack; for example, `require.resolve('./workflows')`. - Workflows are bundled with their dependencies. + - Specify `workflowsPath` pointing to your `workflows.ts` file to pass to Webpack; for example, + `require.resolve('./workflows')`. Workflows are bundled with their dependencies. - If you prefer to handle the bundling yourself, pass a prebuilt bundle to `workflowBundle`. ```ts @@ -271,7 +565,8 @@ run().catch((err) => { }); ``` -`taskQueue` is the only required option; however, use `workflowsPath` and `activities` to register Workflows and Activities with the Worker. +`taskQueue` is the only required option; however, use `workflowsPath` and `activities` to register Workflows and +Activities with the Worker. When scheduling a Workflow, you must specify `taskQueue`. @@ -298,14 +593,18 @@ const worker = await Worker.create({ }); ``` -Optionally, in Workflow code, when calling an Activity, you can specify the Task Queue by passing the `taskQueue` option to `proxyActivities()`, `startChild()`, or `executeChild()`. -If you do not specify `taskQueue`, the TypeScript SDK places Activity and Child Workflow Tasks in the same Task Queue as the Workflow Task Queue. +Optionally, in Workflow code, when calling an Activity, you can specify the Task Queue by passing the `taskQueue` option +to `proxyActivities()`, `startChild()`, or `executeChild()`. If you do not specify `taskQueue`, the TypeScript SDK +places Activity and Child Workflow Tasks in the same Task Queue as the Workflow Task Queue. ### Set a Workflow Id {#workflow-id} -Although it is not required, we recommend providing your own [Workflow Id](/workflow-execution/workflowid-runid#workflow-id)that maps to a business process or business entity identifier, such as an order identifier or customer identifier. +Although it is not required, we recommend providing your own +[Workflow Id](/workflow-execution/workflowid-runid#workflow-id)that maps to a business process or business entity +identifier, such as an order identifier or customer identifier. -Connect to a Client with `client.workflow.start()` and any arguments. Then specify your `taskQueue` and set your `workflowId` to a meaningful business identifier. +Connect to a Client with `client.workflow.start()` and any arguments. Then specify your `taskQueue` and set your +`workflowId` to a meaningful business identifier. ```typescript const handle = await client.workflow.start(example, { @@ -321,24 +620,29 @@ This starts a new Client with the given Workflow Id, Task Queue name, and an arg If the call to start a Workflow Execution is successful, you will gain access to the Workflow Execution's Run Id. -The Workflow Id, Run Id, and Namespace may be used to uniquely identify a Workflow Execution in the system and get its result. +The Workflow Id, Run Id, and Namespace may be used to uniquely identify a Workflow Execution in the system and get its +result. -It's possible to both block progress on the result (synchronous execution) or get the result at some other point in time (asynchronous execution). +It's possible to both block progress on the result (synchronous execution) or get the result at some other point in time +(asynchronous execution). -In the Temporal Platform, it's also acceptable to use Queries as the preferred method for accessing the state and results of Workflow Executions. +In the Temporal Platform, it's also acceptable to use Queries as the preferred method for accessing the state and +results of Workflow Executions. To return the results of a Workflow Execution: ```typescript -return 'Completed ' + wf.workflowInfo().workflowId + ', Total Charged: ' - + totalCharged; +return 'Completed ' + wf.workflowInfo().workflowId + ', Total Charged: ' + totalCharged; ``` -`totalCharged` is just a function declared in your code. For a full example, see [subscription-workflow-project-template-typescript/src/workflows.ts](https://github.com/temporalio/subscription-workflow-project-template-typescript/blob/main/src/workflows.ts). +`totalCharged` is just a function declared in your code. For a full example, see +[subscription-workflow-project-template-typescript/src/workflows.ts](https://github.com/temporalio/subscription-workflow-project-template-typescript/blob/main/src/workflows.ts). -A Workflow function may return a result. If it doesn’t (in which case the return type is `Promise`), the result will be `undefined`. +A Workflow function may return a result. If it doesn’t (in which case the return type is `Promise`), the result +will be `undefined`. -If you started a Workflow with `client.workflow.start()`, you can choose to wait for the result anytime with `handle.result()`. +If you started a Workflow with `client.workflow.start()`, you can choose to wait for the result anytime with +`handle.result()`. ```typescript const handle = client.getHandle(workflowId); @@ -349,7 +653,9 @@ Using a Workflow Handle isn't necessary with `client.workflow.execute()`. Workflows that prematurely end will throw a `WorkflowFailedError` if you call `result()`. -If you call `result()` on a Workflow that prematurely ended for some reason, it throws a [`WorkflowFailedError` error](https://typescript.temporal.io/api/classes/client.WorkflowFailedError/) that reflects the reason. For that reason, it is recommended to catch that error. +If you call `result()` on a Workflow that prematurely ended for some reason, it throws a +[`WorkflowFailedError` error](https://typescript.temporal.io/api/classes/client.WorkflowFailedError/) that reflects the +reason. For that reason, it is recommended to catch that error. ```typescript const handle = client.getHandle(workflowId); From cf46cbdb712d20f4d85d0e029534964bbbd30034 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Thu, 13 Nov 2025 17:21:01 -0800 Subject: [PATCH 2/9] docs: add typescript envconfig --- ...mporal-clients.mdx => temporal-client.mdx} | 455 +++++++++++------- 1 file changed, 289 insertions(+), 166 deletions(-) rename docs/develop/typescript/{temporal-clients.mdx => temporal-client.mdx} (66%) diff --git a/docs/develop/typescript/temporal-clients.mdx b/docs/develop/typescript/temporal-client.mdx similarity index 66% rename from docs/develop/typescript/temporal-clients.mdx rename to docs/develop/typescript/temporal-client.mdx index e94e5bbf22..310e981c1f 100644 --- a/docs/develop/typescript/temporal-clients.mdx +++ b/docs/develop/typescript/temporal-client.mdx @@ -39,7 +39,7 @@ This page shows you how to do the following using the TypeScript SDK with the Te - [Connect to a local development Temporal Service](#connect-to-development-service) - [Connect to Temporal Cloud](#connect-to-temporal-cloud) - [Connect to Temporal Service from a Worker](#connect-to-temporal-service-from-a-worker) -- [Start a Workflow Execution](#start-workflow) +- [Start a Workflow Execution](#start-workflow-execution) - [Get Workflow results](#get-workflow-results) In the TypeScript SDK, connecting to Temporal Service from a Temporal Application and from within an Activity rely on a @@ -51,16 +51,24 @@ connecting from a Worker. ## Connect to development Temporal Service {#connect-to-development-service} -To connect to a development Temporal service from a Temporal Application or from within an Activity, use -[`Connect.connect`](https://typescript.temporal.io/api/classes/client.Connection#connect) to create a Connection object -to connect to the Temporal Service, and pass in that connection when you create a new `Client` instance. You can provide -connections directly in code, load them from **environment variables**, or a **TOML configuration file** using the -`@temporalio/envconfig` helpers. We recommend environment variables or a configuration file for secure, repeatable -configuration. +To connect to a development Temporal service from a Temporal Application or from within an Activity, import the +`Connection` class from `@temporalio/client` and use +[`Connection.connect`](https://typescript.temporal.io/api/classes/client.Connection#connect) to create a Connection +object to connect to the Temporal Service. Then pass in that connection when you create a new `Client` instance. If you +leave the connection options empty, the SDK defaults to connecting to `127.0.0.1:7233` in the `default` Namespace. -When you’re running a Temporal Service locally, such as with the -[Temporal CLI dev server](https://docs.temporal.io/cli/server#start-dev), the required options are minimal. If you don't -specify a host/port, the SDK defaults to `127.0.0.1:7233` and the `default` Namespace. +```ts +import { Connection, Client } from '@temporalio/client'; + +async function run() { + const connection = await Connection.connect(); + const client = new Client({ connection }); +} +``` + +If you need to connect to a Temporal Service with custom options, you can provide connection options directly in code, +load them from **environment variables**, or a **TOML configuration file** using the `@temporalio/envconfig` helpers. We +recommend environment variables or a configuration file for secure, repeatable configuration. @@ -68,11 +76,12 @@ specify a host/port, the SDK defaults to `127.0.0.1:7233` and the `default` Name You can use a TOML configuration file to set connection options for the Temporal Client. The configuration file lets you configure multiple profiles, each with its own set of connection options. You can then specify which profile to use when -creating the Temporal Client. You can use the environment variable `TEMPORAL_CONFIG_FILE` to specify the location of the -TOML file or provide the path to the file directly in code. If you don't provide the configuration file path, the SDK -looks for it at the path `~/.config/temporalio/temporal.toml` or the equivalent on your OS. Refer to -[Environment Configuration](../environment-configuration.mdx#configuration-methods) for more details about configuration -files and profiles. +creating the Temporal Client. + +You can use the environment variable `TEMPORAL_CONFIG_FILE` to specify the location of the TOML file or provide the path +to the file directly in code. If you don't provide the configuration file path, the SDK looks for it at the path +`~/.config/temporalio/temporal.toml` or the equivalent on your OS. Refer to +[Environment Configuration](../environment-configuration.mdx) for more details about configuration files and profiles. :::info @@ -118,10 +127,10 @@ service-version = "v1.2.3" You can create a Temporal Client using a profile from the configuration file as follows. In this example, you load the `default` profile for local development: -{/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "{17-19}"} */} +{/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) -```ts +```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; @@ -170,8 +179,8 @@ main().catch((err) => { -Use the `EnvConfig` package to set connection options for the Temporal Client using environment variables. For a list of -all available environment variables and their default values, refer to +Use the `@temporalio/envconfig` module to set connection options for the Temporal Client using environment variables. +For a list of all available environment variables and their default values, refer to [Environment Configuration](/references/client-environment-configuration). For example, the following code snippet loads all environment variables and creates a Temporal Client with the options @@ -189,39 +198,47 @@ export TEMPORAL_NAMESPACE="default" export TEMPORAL_ADDRESS="localhost:7233" ``` -After setting the environment variables, use the following code to create the Temporal Client: - -```csharp -using Temporalio.Client; -using Temporalio.Client.EnvConfig; - -namespace TemporalioSamples.EnvConfig; - -/// -/// Sample demonstrating loading the default environment configuration profile -/// from a TOML file. -/// -public static class LoadFromFile -{ - public static async Task RunAsync() - { - try - { - var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); - - Console.WriteLine("\nAttempting to connect to client..."); - - var client = await TemporalClient.ConnectAsync(connectOptions); - Console.WriteLine("✅ Client connected successfully!"); - } - catch (Exception ex) when (ex is not OperationCanceledException) - { - Console.WriteLine($"❌ Failed to connect: {ex.Message}"); - } - } +After setting the environment variables, use the following code to create the Temporal Client. Since the environment +variables take precedence, they will override any values set in the configuration file. Therefore, you may leave +`loadClientConnectConfig`'s arguments empty: + +{/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "7,17-18", "selectedLines": ["1-5","17","19","22-40"]} */} +[env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) + +```ts {17-19,28-29} +import { Connection, Client } from '@temporalio/client'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; +import { resolve } from 'path'; + +async function main() { + // ... + const config = loadClientConnectConfig({ + // ... + }); + // ... + console.log(` Address: ${config.connectionOptions.address}`); + console.log(` Namespace: ${config.namespace}`); + console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); + + console.log('\nAttempting to connect to client...'); + try { + const connection = await Connection.connect(config.connectionOptions); + const client = new Client({ connection, namespace: config.namespace }); + console.log('✅ Client connected successfully!'); + await connection.close(); + } catch (err) { + console.log(`❌ Failed to connect: ${err}`); + } } + +main().catch((err) => { + console.error(err); + process.exit(1); +}); ``` +{/* SNIPEND */} + @@ -230,30 +247,16 @@ If you don't want to use environment variables or a configuration file, you can code. This is convenient for local development and testing. You can also load a base configuration from environment variables or a configuration file, and then override specific options in code. -```csharp -using System; -using System.Threading.Tasks; -using Temporalio.Client; - -namespace TemporalioSamples.Manual -{ - public static class ManualConnect - { - public static async Task RunAsync() - { - Console.WriteLine("--- Connecting manually to Temporal ---"); - - var client = await TemporalClient.ConnectAsync(new TemporalClientConnectOptions - { - TargetHost = "localhost:7233", - Namespace = "default", - }); - - Console.WriteLine("✅ Connected to local Temporal service!"); - } - } -} - +```ts +const connection = await Connection.connect({ + address: , + tls: true, + apiKey: , +}); +const client = new Client({ + connection, + namespace: ., +}); ``` @@ -302,12 +305,12 @@ overrides the option set in the configuration file. ::: -For example, the following TOML configuration file defines a `cloud` profile with the necessary connection options to +For example, the following TOML configuration file defines a `staging` profile with the necessary connection options to connect to Temporal Cloud via an API key: ```toml # Cloud profile for Temporal Cloud -[profile.cloud] +[profile.staging] address = "your-namespace.a1b2c.tmprl.cloud:7233" namespace = "your-namespace" api_key = "your-api-key-here" @@ -318,7 +321,7 @@ private key: ```toml # Cloud profile for Temporal Cloud -[profile.cloud] +[profile.staging] address = "your-namespace.a1b2c.tmprl.cloud:7233" namespace = "your-namespace" tls_client_cert_data = "your-tls-client-cert-data" @@ -329,62 +332,56 @@ With the connections options defined in the configuration file, use the `ClientE method to create a Temporal Client using the `staging` profile as follows. After loading the profile, you can also programmatically override specific connection options before creating the client. -```csharp title="LoadProfile.cs" {25. 41} -using Temporalio.Client; -using Temporalio.Client.EnvConfig; - -namespace TemporalioSamples.EnvConfig; - -/// -/// Sample demonstrating loading a named environment configuration profile and -/// programmatically overriding its values. -/// -public static class LoadProfile -{ - public static async Task RunAsync() - { - Console.WriteLine("--- Loading 'staging' profile with programmatic overrides ---"); - - try - { - var configFile = Path.Combine(Directory.GetCurrentDirectory(), "config.toml"); - var profileName = "staging"; - - Console.WriteLine("The 'staging' profile in config.toml has an incorrect address (localhost:9999)."); - Console.WriteLine("We'll programmatically override it to the correct address."); - - // Load the 'staging' profile - var connectOptions = ClientEnvConfig.LoadClientConnectOptions(new ClientEnvConfig.ProfileLoadOptions - { - Profile = profileName, - ConfigSource = DataSource.FromPath(configFile), - }); - - // Override the target host to the correct address. - // This is the recommended way to override configuration values. - connectOptions.TargetHost = "localhost:7233"; - - Console.WriteLine($"\nLoaded '{profileName}' profile from {configFile} with overrides."); - Console.WriteLine($" Address: {connectOptions.TargetHost} (overridden from localhost:9999)"); - Console.WriteLine($" Namespace: {connectOptions.Namespace}"); - - Console.WriteLine("\nAttempting to connect to client..."); - - var client = await TemporalClient.ConnectAsync(connectOptions); - Console.WriteLine("✅ Client connected successfully!"); - - // Test the connection by checking the service - var sysInfo = await client.Connection.WorkflowService.GetSystemInfoAsync(new()); - Console.WriteLine("✅ Successfully verified connection to Temporal server!\n{0}", sysInfo); - } - catch (Exception ex) when (ex is not OperationCanceledException) - { - Console.WriteLine($"❌ Failed to connect: {ex.Message}"); - } - } +{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "25,41"} */} +[env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) + +```ts {25,41} +import { Connection, Client } from '@temporalio/client'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; +import { resolve } from 'path'; + +async function main() { + console.log("--- Loading 'staging' profile with programmatic overrides ---"); + + const configFile = resolve(__dirname, '../config.toml'); + const profileName = 'staging'; + + // The 'staging' profile in config.toml has an incorrect address (localhost:9999) + // We'll programmatically override it to the correct address + + // Load the 'staging' profile. + const config = loadClientConnectConfig({ + profile: profileName, + configSource: { path: configFile }, + }); + + // Override the target host to the correct address. + // This is the recommended way to override configuration values. + config.connectionOptions.address = 'localhost:7233'; + + console.log(`\nLoaded '${profileName}' profile from ${configFile} with overrides.`); + console.log(` Address: ${config.connectionOptions.address} (overridden from localhost:9999)`); + console.log(` Namespace: ${config.namespace}`); + + console.log('\nAttempting to connect to client...'); + try { + const connection = await Connection.connect(config.connectionOptions); + const client = new Client({ connection, namespace: config.namespace }); + console.log('✅ Client connected successfully!'); + await connection.close(); + } catch (err) { + console.log(`❌ Failed to connect: ${err}`); + } } + +main().catch((err) => { + console.error(err); + process.exit(1); +}); ``` +{/* SNIPEND */} + @@ -412,37 +409,43 @@ specified in those variables. If you have defined a configuration file at either variable, this will also load the default profile in the configuration file. However, any options set via environment variables will take precedence. -```csharp {16,20} -using Temporalio.Client; -using Temporalio.Client.EnvConfig; - -namespace TemporalioSamples.EnvConfig; - -/// -/// Sample demonstrating loading the default environment configuration profile -/// from a TOML file. -/// -public static class LoadFromFile -{ - public static async Task RunAsync() - { - try - { - var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); - - Console.WriteLine("\nAttempting to connect to client..."); - - var client = await TemporalClient.ConnectAsync(connectOptions); - Console.WriteLine("✅ Client connected successfully!"); - } - catch (Exception ex) when (ex is not OperationCanceledException) - { - Console.WriteLine($"❌ Failed to connect: {ex.Message}"); - } - } +{/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29", "selectedLines": ["1-5","17","19","22-40"]} */} +[env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) + +```ts {17-19,28-29} +import { Connection, Client } from '@temporalio/client'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; +import { resolve } from 'path'; + +async function main() { + // ... + const config = loadClientConnectConfig({ + // ... + }); + // ... + console.log(` Address: ${config.connectionOptions.address}`); + console.log(` Namespace: ${config.namespace}`); + console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); + + console.log('\nAttempting to connect to client...'); + try { + const connection = await Connection.connect(config.connectionOptions); + const client = new Client({ connection, namespace: config.namespace }); + console.log('✅ Client connected successfully!'); + await connection.close(); + } catch (err) { + console.log(`❌ Failed to connect: ${err}`); + } } + +main().catch((err) => { + console.error(err); + process.exit(1); +}); ``` +{/* SNIPEND */} + @@ -450,19 +453,139 @@ public static class LoadFromFile You can also provide connections options in your Go code directly. To create an initial connection, provide the Namespace and API key values to the ` TemporalClient.ConnectAsync` method. -```csharp -var myClient = TemporalClient.ConnectAsync(new() -{ - Namespace = ".", - ApiKey = "", - Tls = new(), +```ts +const connection = await Connection.connect({ + address: , + tls: true, + apiKey: , +}); +const client = new Client({ + connection, + namespace: ., +}); +``` + +To update an API key, use the `setApiKey` method on the Connection object: + +```ts +connection.setApiKey(); +``` + + + + + +## Connect to Temporal Service from a Worker {#connect-to-temporal-service-from-a-worker} + +Connecting to Temporal Service from a Worker requires the same set of connections options as connecting from a Temporal +Application or from within an Activity, but the connection type is different. When connecting from a Worker, you create +a `NativeConnection` object instead of a `Connection` object. The `NativeConnection` class is imported from +`@temporalio/worker` instead of `@temporalio/client`. After you create the `NativeConnection` object, you pass it to +`Worker.create()` when creating the Worker. + +To provide connection options to the `NativeConnection`, you can use environment variables, a configuration file, or +directly in code. The following code snippets show how to create a `NativeConnection` object using each method. Refer to +[Connect to a local development Temporal Service](#connect-to-development-service) and +[Connect to Temporal Cloud](#connect-to-temporal-cloud) for details on how to provide connection options using each +method. + + + + + +Ensure you have a TOML configuration file with the necessary connection options defined. For example, the following TOML +configuration file defines a `staging` profile with the necessary connection options to connect to Temporal Cloud via an +API key: + +```toml +# Cloud profile for Temporal Cloud +[profile.staging] +address = "your-namespace.a1b2c.tmprl.cloud:7233" +namespace = "your-namespace" +api_key = "your-api-key-here" +``` + +Use the `loadClientConnectConfig` helper from `@temporalio/envconfig` to load the `staging` profile from the +configuration file and create a `NativeConnection` object as follows: + +```ts {1,30} +import { NativeConnection } from '@temporalio/worker'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; +import { resolve } from 'path'; + +async function main() { + const configFile = resolve(__dirname, '../config.toml'); + const profileName = 'staging' + + // Load the 'staging' profile. + const config = loadClientConnectConfig({ + profile: profileName, + configSource: { path: configFile }, + }); + + const worker = await Worker.create({ + connection, + namespace: ., + // ... }); +} +``` + + + + + +Ensure you have set the necessary environment variables to connect to Temporal Cloud. For example: + +```bash +export TEMPORAL_NAMESPACE="your-namespace.your-account-id" +export TEMPORAL_ADDRESS="your-namespace.a1b2c.tmprl.cloud:7233" +export TEMPORAL_TLS_CLIENT_CERT_PATH="/path/to/your/client/cert.pem" +export TEMPORAL_TLS_CLIENT_KEY_PATH="/path/to/your/client/key.pem" +``` + +After setting the environment variables, use the following code to create a `NativeConnection` object using the +`loadClientConnectConfig` helper from `@temporalio/envconfig`: + +```ts {1,5} +import { NativeConnection } from '@temporalio/worker'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; + +async function main() { + const config = loadClientConnectConfig(); + + const connection = await NativeConnection.connect(config); + + const worker = await Worker.create({ + connection, + namespace: process.env.TEMPORAL_NAMESPACE, + // ... + }); +} ``` -To update an API key, update the value of `ApiKey` on the existing client connection: + + + + +You can also provide connections options in your TypeScript code directly. To create an initial connection, provide the +connections to the ` NativeConnection.connect` method, and then pass the resulting `NativeConnection` object to +`Worker.create()` when creating the Worker: -```csharp -myClient.Connection.ApiKey = myKeyUpdated; +```ts {1,4,9} +import { NativeConnection } from '@temporalio/worker'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; + +const connection = await NativeConnection.connect({ + address: , + tls: true, + apiKey: , +}); +const worker = await Worker.create({ + connection, + namespace: ., + // ... +}); ``` From f400b6a446951d1cc6c7694a60082d59669fb301 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Thu, 13 Nov 2025 17:27:55 -0800 Subject: [PATCH 3/9] docs: add typescript to envconfig --- docs/develop/environment-configuration.mdx | 109 ++++++++++++++++++++ docs/develop/typescript/temporal-client.mdx | 22 ++-- 2 files changed, 116 insertions(+), 15 deletions(-) diff --git a/docs/develop/environment-configuration.mdx b/docs/develop/environment-configuration.mdx index f782f24749..a3ef51a25a 100644 --- a/docs/develop/environment-configuration.mdx +++ b/docs/develop/environment-configuration.mdx @@ -330,6 +330,59 @@ public static class LoadFromFile ``` + + +To load the `default` profile along with any environment variables in TypeScript, use the `loadClientConnectConfig` helper from `@temporalio/envconfig` package. + +{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "25,41"} */} +[env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) +```ts {25,41} +import { Connection, Client } from '@temporalio/client'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; +import { resolve } from 'path'; + +async function main() { + console.log("--- Loading 'staging' profile with programmatic overrides ---"); + + const configFile = resolve(__dirname, '../config.toml'); + const profileName = 'staging'; + + // The 'staging' profile in config.toml has an incorrect address (localhost:9999) + // We'll programmatically override it to the correct address + + // Load the 'staging' profile. + const config = loadClientConnectConfig({ + profile: profileName, + configSource: { path: configFile }, + }); + + // Override the target host to the correct address. + // This is the recommended way to override configuration values. + config.connectionOptions.address = 'localhost:7233'; + + console.log(`\nLoaded '${profileName}' profile from ${configFile} with overrides.`); + console.log(` Address: ${config.connectionOptions.address} (overridden from localhost:9999)`); + console.log(` Namespace: ${config.namespace}`); + + console.log('\nAttempting to connect to client...'); + try { + const connection = await Connection.connect(config.connectionOptions); + const client = new Client({ connection, namespace: config.namespace }); + console.log('✅ Client connected successfully!'); + await connection.close(); + } catch (err) { + console.log(`❌ Failed to connect: ${err}`); + } +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); +``` +{/* SNIPEND */} + + ## Load configuration from a custom path @@ -570,4 +623,60 @@ public static class LoadProfile + + +To load a specific profile from a custom path in TypeScript, use the `loadClientConnectConfig` helper from +`@temporalio/envconfig` package with the `profile` and `configFile` options. + +{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "25,41"} */} +[env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) +```ts {25,41} +import { Connection, Client } from '@temporalio/client'; +import { loadClientConnectConfig } from '@temporalio/envconfig'; +import { resolve } from 'path'; + +async function main() { + console.log("--- Loading 'staging' profile with programmatic overrides ---"); + + const configFile = resolve(__dirname, '../config.toml'); + const profileName = 'staging'; + + // The 'staging' profile in config.toml has an incorrect address (localhost:9999) + // We'll programmatically override it to the correct address + + // Load the 'staging' profile. + const config = loadClientConnectConfig({ + profile: profileName, + configSource: { path: configFile }, + }); + + // Override the target host to the correct address. + // This is the recommended way to override configuration values. + config.connectionOptions.address = 'localhost:7233'; + + console.log(`\nLoaded '${profileName}' profile from ${configFile} with overrides.`); + console.log(` Address: ${config.connectionOptions.address} (overridden from localhost:9999)`); + console.log(` Namespace: ${config.namespace}`); + + console.log('\nAttempting to connect to client...'); + try { + const connection = await Connection.connect(config.connectionOptions); + const client = new Client({ connection, namespace: config.namespace }); + console.log('✅ Client connected successfully!'); + await connection.close(); + } catch (err) { + console.log(`❌ Failed to connect: ${err}`); + } +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); +``` +{/* SNIPEND */} + + + +``` diff --git a/docs/develop/typescript/temporal-client.mdx b/docs/develop/typescript/temporal-client.mdx index 310e981c1f..d14bb2f1bb 100644 --- a/docs/develop/typescript/temporal-client.mdx +++ b/docs/develop/typescript/temporal-client.mdx @@ -129,7 +129,6 @@ You can create a Temporal Client using a profile from the configuration file as {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) - ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; @@ -172,7 +171,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} @@ -204,18 +202,17 @@ variables take precedence, they will override any values set in the configuratio {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "7,17-18", "selectedLines": ["1-5","17","19","22-40"]} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) - -```ts {17-19,28-29} +```ts {7,17-18} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { - // ... +// ... const config = loadClientConnectConfig({ - // ... +// ... }); - // ... +// ... console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); @@ -236,7 +233,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} @@ -334,7 +330,6 @@ programmatically override specific connection options before creating the client {/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "25,41"} */} [env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) - ```ts {25,41} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; @@ -379,7 +374,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} @@ -411,18 +405,17 @@ variables will take precedence. {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29", "selectedLines": ["1-5","17","19","22-40"]} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) - ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { - // ... +// ... const config = loadClientConnectConfig({ - // ... +// ... }); - // ... +// ... console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); @@ -443,7 +436,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} From feed81ec0d682feee9c56a2d5206bf5c99a7cd26 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Thu, 13 Nov 2025 17:34:39 -0800 Subject: [PATCH 4/9] docs: update line highlights --- docs/develop/environment-configuration.mdx | 12 +++++--- docs/develop/typescript/temporal-client.mdx | 32 ++++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/docs/develop/environment-configuration.mdx b/docs/develop/environment-configuration.mdx index a3ef51a25a..7d515ba5a7 100644 --- a/docs/develop/environment-configuration.mdx +++ b/docs/develop/environment-configuration.mdx @@ -334,9 +334,10 @@ public static class LoadFromFile To load the `default` profile along with any environment variables in TypeScript, use the `loadClientConnectConfig` helper from `@temporalio/envconfig` package. -{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "25,41"} */} +{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "15-18,30-31"} */} [env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) -```ts {25,41} + +```ts {15-18,30-31} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; @@ -380,6 +381,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */} @@ -628,9 +630,10 @@ public static class LoadProfile To load a specific profile from a custom path in TypeScript, use the `loadClientConnectConfig` helper from `@temporalio/envconfig` package with the `profile` and `configFile` options. -{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "25,41"} */} +{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "15-18,30-31"} */} [env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) -```ts {25,41} + +```ts {15-18,30-31} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; @@ -674,6 +677,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */} diff --git a/docs/develop/typescript/temporal-client.mdx b/docs/develop/typescript/temporal-client.mdx index d14bb2f1bb..16c9143c5b 100644 --- a/docs/develop/typescript/temporal-client.mdx +++ b/docs/develop/typescript/temporal-client.mdx @@ -129,6 +129,7 @@ You can create a Temporal Client using a profile from the configuration file as {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) + ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; @@ -171,6 +172,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */} @@ -202,17 +204,18 @@ variables take precedence, they will override any values set in the configuratio {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "7,17-18", "selectedLines": ["1-5","17","19","22-40"]} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) + ```ts {7,17-18} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { -// ... + // ... const config = loadClientConnectConfig({ -// ... + // ... }); -// ... + // ... console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); @@ -233,6 +236,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */} @@ -324,13 +328,16 @@ tls_client_cert_data = "your-tls-client-cert-data" tls_client_key_path = "your-tls-client-key-path" ``` -With the connections options defined in the configuration file, use the `ClientEnvConfig.LoadClientConnectOptions` -method to create a Temporal Client using the `staging` profile as follows. After loading the profile, you can also -programmatically override specific connection options before creating the client. +With the connections options defined in the configuration file, use the `loadClientConnectConfig` helper from +`@temporalio/envconfig` to load the `staging` profile from the configuration file. You can then pass the resulting +configuration to the `Connection.connect` method to create a Temporal Client using the `staging` profile as follows. +After loading the profile, you can also programmatically override specific connection options before creating the +client. -{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "25,41"} */} +{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "15-18,30-31"} */} [env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) -```ts {25,41} + +```ts {15-18,30-31} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; @@ -374,6 +381,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */} @@ -405,17 +413,18 @@ variables will take precedence. {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29", "selectedLines": ["1-5","17","19","22-40"]} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) + ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { -// ... + // ... const config = loadClientConnectConfig({ -// ... + // ... }); -// ... + // ... console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); @@ -436,6 +445,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */} From f4bc3efd75ac621f7e3d0267b3aa8d3d92f2e806 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 14 Nov 2025 11:58:49 -0800 Subject: [PATCH 5/9] docs: add additional explanation --- docs/develop/typescript/temporal-client.mdx | 57 ++++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/docs/develop/typescript/temporal-client.mdx b/docs/develop/typescript/temporal-client.mdx index 16c9143c5b..ba917fd2f6 100644 --- a/docs/develop/typescript/temporal-client.mdx +++ b/docs/develop/typescript/temporal-client.mdx @@ -70,7 +70,7 @@ If you need to connect to a Temporal Service with custom options, you can provid load them from **environment variables**, or a **TOML configuration file** using the `@temporalio/envconfig` helpers. We recommend environment variables or a configuration file for secure, repeatable configuration. - + @@ -189,9 +189,9 @@ specified in those variables. If you have defined a configuration file at either variable, this will also load the default profile in the configuration file. However, any options set via environment variables will take precedence. -Set the following environment variables before running your .NET application. Replace the placeholder values with your -actual configuration. Since this is for a local development Temporal Service, the values connect to `localhost:7233` and -the `default` Namespace. You may omit these variables entirely since they're the defaults. +Set the following environment variables before running your application. Replace the placeholder values with your actual +configuration. Since this is for a local development Temporal Service, the values connect to `localhost:7233` and the +`default` Namespace. You may omit these variables entirely since they're the defaults. ```bash export TEMPORAL_NAMESPACE="default" @@ -284,7 +284,7 @@ You can find the Namespace and Account ID, as well as the endpoint, on the Names You can provide these connection options using environment variables, a configuration file, or directly in code. - + @@ -330,9 +330,9 @@ tls_client_key_path = "your-tls-client-key-path" With the connections options defined in the configuration file, use the `loadClientConnectConfig` helper from `@temporalio/envconfig` to load the `staging` profile from the configuration file. You can then pass the resulting -configuration to the `Connection.connect` method to create a Temporal Client using the `staging` profile as follows. -After loading the profile, you can also programmatically override specific connection options before creating the -client. +configuration to the `Connection.connect` method. After that, you then pass the `connection` object and the Namespace to +the `Client` constructor to create a Temporal Client using the `staging` profile as follows. After loading the profile, +you can also programmatically override specific connection options before creating the client. {/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "15-18,30-31"} */} [env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) @@ -398,7 +398,7 @@ The following environment variables are required to connect to Temporal Cloud: - `TEMPORAL_TLS_CLIENT_KEY_DATA` or `TEMPORAL_TLS_CLIENT_KEY_PATH`: Your mTLS client private key data or file path. Required if you are using mTLS authentication. -Ensure these environment variables exist in your environment before running your .NET application. +Ensure these environment variables exist in your environment before running your application. Import the `EnvConfig` package to set connection options for the Temporal Client using environment variables. The `MustLoadDefaultClientOptions` function will automatically load all environment variables. For a list of all available @@ -452,8 +452,8 @@ main().catch((err) => { -You can also provide connections options in your Go code directly. To create an initial connection, provide the -Namespace and API key values to the ` TemporalClient.ConnectAsync` method. +You can also provide connections options in your code directly. To create an initial connection, provide the Namespace +and API key values to the `Connection.connect` method. ```ts const connection = await Connection.connect({ @@ -510,7 +510,7 @@ api_key = "your-api-key-here" Use the `loadClientConnectConfig` helper from `@temporalio/envconfig` to load the `staging` profile from the configuration file and create a `NativeConnection` object as follows: -```ts {1,30} +```ts {1,15,17} import { NativeConnection } from '@temporalio/worker'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; @@ -525,6 +525,8 @@ async function main() { configSource: { path: configFile }, }); + const connection = await NativeConnection.connect(config.connectionOptions); + const worker = await Worker.create({ connection, namespace: ., @@ -556,7 +558,7 @@ import { loadClientConnectConfig } from '@temporalio/envconfig'; async function main() { const config = loadClientConnectConfig(); - const connection = await NativeConnection.connect(config); + const connection = await NativeConnection.connect(config.connectionOptions); const worker = await Worker.create({ connection, @@ -594,6 +596,35 @@ const worker = await Worker.create({ +## NativeConnection, Connection, and Client + +`NativeConnection`, `Connection`, and `Client` are all classes provided by the TypeScript SDK to facilitate +communication with the Temporal Service. This section explains the differences between these classes and their +respective use cases. For detailed information about each class, refer to the +[Temporal TypeScript API documentation](https://typescript.temporal.io/api/namespaces/client). + +### NativeConnection vs. Connection {#native-connection-vs-connection} + +The TypeScript SDK provides two types of connection classes to connect to the Temporal Service: `NativeConnection` and +`Connection`. The `NativeConnection` class is used to connect from a Worker, while the `Connection` class is used to +connect from a Temporal Application or from within an Activity, typically through a `Client` object. Both connection +classes accept the same set of connection options. + +### Connection vs. Client {#connection-vs-client} + +A `Client` object is a high-level, lightweight abstraction that simplifies interaction with the Temporal Service. It +internally manages a `Connection` object to handle the low-level communication details. The `Client` class provides +convenient methods for common operations such as starting Workflow Executions, sending Signals and Queries, and +retrieving Workflow results. + +A `Connection` object is a lower-level and expensive object that represents a direct connection to the Temporal Service. +You pass in a `Connection` object to the `Client` constructor to create a `Client` instance. Since a `Connection` is +expensive to create, create a single `Connection` object and reuse it across your application whenever possible. + +When instantiating a `Connection`, you specify most connection options except for the Namespace, such as the Temporal +Service endpoint, TLS settings, and authentication credentials. When instantiating a `Client`, you provide the +`Connection` object and the Namespace you want to connect to, along with other client options. + ## Start Workflow Execution {#start-workflow-execution} **How to start a Workflow Execution using the Typescript SDK** From 0427dd2bf768982a6c77ecd82102c8a34ecab820 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 14 Nov 2025 12:04:01 -0800 Subject: [PATCH 6/9] docs: add links --- docs/develop/typescript/temporal-client.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/develop/typescript/temporal-client.mdx b/docs/develop/typescript/temporal-client.mdx index ba917fd2f6..9eb6f4ad1d 100644 --- a/docs/develop/typescript/temporal-client.mdx +++ b/docs/develop/typescript/temporal-client.mdx @@ -52,7 +52,7 @@ connecting from a Worker. ## Connect to development Temporal Service {#connect-to-development-service} To connect to a development Temporal service from a Temporal Application or from within an Activity, import the -`Connection` class from `@temporalio/client` and use +[`Connection` class](https://typescript.temporal.io/api/classes/client.Connection) from `@temporalio/client` and use [`Connection.connect`](https://typescript.temporal.io/api/classes/client.Connection#connect) to create a Connection object to connect to the Temporal Service. Then pass in that connection when you create a new `Client` instance. If you leave the connection options empty, the SDK defaults to connecting to `127.0.0.1:7233` in the `default` Namespace. @@ -481,7 +481,8 @@ connection.setApiKey(); Connecting to Temporal Service from a Worker requires the same set of connections options as connecting from a Temporal Application or from within an Activity, but the connection type is different. When connecting from a Worker, you create -a `NativeConnection` object instead of a `Connection` object. The `NativeConnection` class is imported from +a `NativeConnection` object instead of a `Connection` object. The +[`NativeConnection` class](https://typescript.temporal.io/api/classes/worker.NativeConnection) is imported from `@temporalio/worker` instead of `@temporalio/client`. After you create the `NativeConnection` object, you pass it to `Worker.create()` when creating the Worker. From c37bf17d804a8847c6f3446ec8d14aacaefba960 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 14 Nov 2025 12:09:23 -0800 Subject: [PATCH 7/9] docs: fix broken link --- docs/develop/typescript/temporal-client.mdx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/develop/typescript/temporal-client.mdx b/docs/develop/typescript/temporal-client.mdx index 9eb6f4ad1d..5f6aed4437 100644 --- a/docs/develop/typescript/temporal-client.mdx +++ b/docs/develop/typescript/temporal-client.mdx @@ -28,11 +28,10 @@ tags: - Certificates --- -A [Temporal Client](https://chatgpt.com/encyclopedia/temporal-sdks#temporal-client) enables you to communicate with the -Temporal Service. Communication with a Temporal Service lets you perform actions such as starting Workflow Executions, -sending Signals and Queries to Workflow Executions, getting Workflow results, and more. You cannot initialize a Temporal -Client inside a Workflow. However, they're commonly initialized inside an Activity to communicate with a Temporal -Service. +A [Temporal Client](/encyclopedia/temporal-sdks#temporal-client) enables you to communicate with the Temporal Service. +Communication with a Temporal Service lets you perform actions such as starting Workflow Executions, sending Signals and +Queries to Workflow Executions, getting Workflow results, and more. You cannot initialize a Temporal Client inside a +Workflow. However, they're commonly initialized inside an Activity to communicate with a Temporal Service. This page shows you how to do the following using the TypeScript SDK with the Temporal Client: From 5e0de77f4051f309bb729357dd728309de2fd041 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 14 Nov 2025 14:07:48 -0800 Subject: [PATCH 8/9] docs: update samples --- docs/develop/environment-configuration.mdx | 64 +++++++++------------ docs/develop/typescript/temporal-client.mdx | 20 ++----- 2 files changed, 34 insertions(+), 50 deletions(-) diff --git a/docs/develop/environment-configuration.mdx b/docs/develop/environment-configuration.mdx index d1cc13cc3a..3c30b41a24 100644 --- a/docs/develop/environment-configuration.mdx +++ b/docs/develop/environment-configuration.mdx @@ -334,36 +334,33 @@ public static class LoadFromFile To load the `default` profile along with any environment variables in TypeScript, use the `loadClientConnectConfig` helper from `@temporalio/envconfig` package. -{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "15-18,30-31"} */} -[env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) - -```ts {15-18,30-31} +{/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} +[env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) +```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { - console.log("--- Loading 'staging' profile with programmatic overrides ---"); + console.log('--- Loading default profile from config.toml ---'); + // For this sample to be self-contained, we explicitly provide the path to + // the config.toml file included in this directory. + // By default though, the config.toml file will be loaded from + // ~/.config/temporalio/temporal.toml (or the equivalent standard config directory on your OS). const configFile = resolve(__dirname, '../config.toml'); - const profileName = 'staging'; - - // The 'staging' profile in config.toml has an incorrect address (localhost:9999) - // We'll programmatically override it to the correct address - // Load the 'staging' profile. + // loadClientConnectConfig is a helper that loads a profile and prepares + // the configuration for Connection.connect and Client. By default, it loads the + // "default" profile. const config = loadClientConnectConfig({ - profile: profileName, configSource: { path: configFile }, }); - // Override the target host to the correct address. - // This is the recommended way to override configuration values. - config.connectionOptions.address = 'localhost:7233'; - - console.log(`\nLoaded '${profileName}' profile from ${configFile} with overrides.`); - console.log(` Address: ${config.connectionOptions.address} (overridden from localhost:9999)`); + console.log(`Loaded 'default' profile from ${configFile}.`); + console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); + console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); console.log('\nAttempting to connect to client...'); try { @@ -381,7 +378,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} @@ -630,36 +626,33 @@ public static class LoadProfile To load a specific profile from a custom path in TypeScript, use the `loadClientConnectConfig` helper from `@temporalio/envconfig` package with the `profile` and `configFile` options. -{/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "15-18,30-31"} */} -[env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) - -```ts {15-18,30-31} +{/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} +[env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) +```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { - console.log("--- Loading 'staging' profile with programmatic overrides ---"); + console.log('--- Loading default profile from config.toml ---'); + // For this sample to be self-contained, we explicitly provide the path to + // the config.toml file included in this directory. + // By default though, the config.toml file will be loaded from + // ~/.config/temporalio/temporal.toml (or the equivalent standard config directory on your OS). const configFile = resolve(__dirname, '../config.toml'); - const profileName = 'staging'; - - // The 'staging' profile in config.toml has an incorrect address (localhost:9999) - // We'll programmatically override it to the correct address - // Load the 'staging' profile. + // loadClientConnectConfig is a helper that loads a profile and prepares + // the configuration for Connection.connect and Client. By default, it loads the + // "default" profile. const config = loadClientConnectConfig({ - profile: profileName, configSource: { path: configFile }, }); - // Override the target host to the correct address. - // This is the recommended way to override configuration values. - config.connectionOptions.address = 'localhost:7233'; - - console.log(`\nLoaded '${profileName}' profile from ${configFile} with overrides.`); - console.log(` Address: ${config.connectionOptions.address} (overridden from localhost:9999)`); + console.log(`Loaded 'default' profile from ${configFile}.`); + console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); + console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); console.log('\nAttempting to connect to client...'); try { @@ -677,7 +670,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} diff --git a/docs/develop/typescript/temporal-client.mdx b/docs/develop/typescript/temporal-client.mdx index 5f6aed4437..84f3c2d85b 100644 --- a/docs/develop/typescript/temporal-client.mdx +++ b/docs/develop/typescript/temporal-client.mdx @@ -128,7 +128,6 @@ You can create a Temporal Client using a profile from the configuration file as {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) - ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; @@ -171,7 +170,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} @@ -203,18 +201,17 @@ variables take precedence, they will override any values set in the configuratio {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "7,17-18", "selectedLines": ["1-5","17","19","22-40"]} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) - ```ts {7,17-18} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { - // ... +// ... const config = loadClientConnectConfig({ - // ... +// ... }); - // ... +// ... console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); @@ -235,7 +232,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} @@ -335,7 +331,6 @@ you can also programmatically override specific connection options before creati {/* SNIPSTART typescript-env-config-load-profile-with-overrides {"highlightedLines": "15-18,30-31"} */} [env-config/src/load-profile.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-profile.ts) - ```ts {15-18,30-31} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; @@ -380,7 +375,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} @@ -412,18 +406,17 @@ variables will take precedence. {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29", "selectedLines": ["1-5","17","19","22-40"]} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) - ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; import { resolve } from 'path'; async function main() { - // ... +// ... const config = loadClientConnectConfig({ - // ... +// ... }); - // ... +// ... console.log(` Address: ${config.connectionOptions.address}`); console.log(` Namespace: ${config.namespace}`); console.log(` gRPC Metadata: ${JSON.stringify(config.connectionOptions.metadata)}`); @@ -444,7 +437,6 @@ main().catch((err) => { process.exit(1); }); ``` - {/* SNIPEND */} From b3298ee9439297f4c5fb7dcc915dd7e1bacd85c1 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 14 Nov 2025 14:09:06 -0800 Subject: [PATCH 9/9] docs: add TS to public preview comment --- docs/develop/environment-configuration.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/develop/environment-configuration.mdx b/docs/develop/environment-configuration.mdx index 3c30b41a24..29d603f201 100644 --- a/docs/develop/environment-configuration.mdx +++ b/docs/develop/environment-configuration.mdx @@ -27,7 +27,7 @@ refer to [Temporal Client Environment Configuration Reference](../references/cli Environment configuration is in [Public Preview](../evaluate/development-production-features/release-stages.mdx#public-preview) in the Temporal Go, -Python, Ruby, and .NET SDKs, as well as the Temporal CLI. +Python, Ruby, TypeScript, and .NET SDKs, as well as the Temporal CLI. ::: @@ -336,6 +336,7 @@ To load the `default` profile along with any environment variables in TypeScript {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) + ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; @@ -378,6 +379,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */} @@ -628,6 +630,7 @@ To load a specific profile from a custom path in TypeScript, use the `loadClient {/* SNIPSTART typescript-env-config-load-default-profile {"highlightedLines": "17-19,28-29"} */} [env-config/src/load-from-file.ts](https://github.com/temporalio/samples-typescript/blob/main/env-config/src/load-from-file.ts) + ```ts {17-19,28-29} import { Connection, Client } from '@temporalio/client'; import { loadClientConnectConfig } from '@temporalio/envconfig'; @@ -670,6 +673,7 @@ main().catch((err) => { process.exit(1); }); ``` + {/* SNIPEND */}