This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Dapr Configuration JavaScript SDK

Read application configuration and subscribe to changes with the Dapr JavaScript SDK

Use DaprClient.configuration to retrieve application configuration from a configured store and subscribe to changes. You can read selected keys or all keys, watch selected keys or the whole store, and pass store-specific request metadata.

These guides target @dapr/dapr 3.19.0 or later. Configuration operations in the JavaScript SDK require gRPC; the HTTP client throws HTTPNotSupportedError for both reads and subscriptions, even though the Dapr runtime exposes an HTTP Configuration API.

The client reads and watches configuration; it does not create, update, or delete configuration items. Make those changes directly in the backing store using its own tools or API. This building block manages application settings, not the Dapr runtime’s Configuration resource.

Start with the configuration how-to guide for a runnable example. Refer to the client usage guidance for the complete method surface, metadata, response shapes, subscription lifetime, and error handling.

1 - How to: Manage configuration with the Dapr JavaScript SDK

Retrieve application settings and subscribe to configuration changes using the Dapr JavaScript SDK

Use DaprClient.configuration to read application settings and react to changes without calling the configuration store directly. This guide uses Redis for local development.

Prerequisites

In your application directory, initialize an npm project if needed and install the SDK:

npm init -y
npm install @dapr/dapr@3.19.0

The examples use JavaScript ES modules saved as .mjs files and do not require a TypeScript compiler. The same APIs are available in TypeScript.

Configure a store

Start a dedicated local Redis instance on port 6380 to avoid conflicting with Redis started by dapr init:

docker run --name js-config-redis -p 127.0.0.1:6380:6379 -d redis:7

The Redis configuration component enables the keyspace notifications it needs when a subscription starts, so no extra Redis flags are required. This example has no authentication and binds Redis to loopback for local development only. For production, configure authentication and TLS as described in the Redis configuration store reference.

Create a components directory in your application directory and save the following as components\configstore.yaml:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: configstore
spec:
  type: configuration.redis
  version: v1
  metadata:
    - name: redisHost
      value: "localhost:6380"
    - name: redisPassword
      value: ""

The component’s metadata.name, configstore, is the store name supplied to each SDK method. A Redis state store component is not a configuration store; the component type must be configuration.redis.

Populate the store:

docker exec js-config-redis redis-cli MSET orderId1 "101||1" orderId2 "102||1"

This example uses Redis configuration values in value||version format. The SDK returns the value and version as separate strings.

Retrieve configuration items

Save the following as get-configuration.mjs:

import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr";

const client = new DaprClient({
  daprHost: "127.0.0.1",
  daprPort: process.env.DAPR_GRPC_PORT ?? "50001",
  communicationProtocol: CommunicationProtocolEnum.GRPC,
});

try {
  const config = await client.configuration.get(
    "configstore",
    ["orderId1", "orderId2"],
  );

  for (const [key, item] of Object.entries(config.items)) {
    console.log(`${key}: ${item.value} (version ${item.version})`);
  }

  const order = config.items["orderId1"];
  if (order === undefined) {
    throw new Error("Required configuration key orderId1 was not returned");
  }
} finally {
  await client.stop();
}

Run it with a Dapr sidecar:

dapr run --app-id js-config-reader --dapr-grpc-port 50001 --resources-path .\components -- node get-configuration.mjs

For the seeded values, the application prints:

orderId1: 101 (version 1)
orderId2: 102 (version 1)

To request all items instead, omit the keys argument or pass an empty array:

const config = await client.configuration.get("configstore");

An empty key list is sent to the component. Whether it can return all keys, and which keys are included, depends on the store. Check required keys explicitly rather than assuming every requested key is present.

Subscribe to configuration changes

Save the following as watch-configuration.mjs:

import { setTimeout } from "node:timers/promises";
import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr";

const client = new DaprClient({
  daprHost: "127.0.0.1",
  daprPort: process.env.DAPR_GRPC_PORT ?? "50001",
  communicationProtocol: CommunicationProtocolEnum.GRPC,
});

let subscription;

try {
  subscription = await client.configuration.subscribeWithKeys(
    "configstore",
    ["orderId1", "orderId2"],
    async (changes) => {
      for (const [key, item] of Object.entries(changes.items)) {
        console.log(`${key}: ${item.value} (version ${item.version})`);
      }
    },
  );

  console.log("Subscription requested; watching for 60 seconds");
  await setTimeout(60_000);
} finally {
  try {
    if (subscription) {
      await subscription.stop();
    }
  } finally {
    await client.stop();
  }
}

Run the subscriber after the reader has exited:

dapr run --app-id js-config-watcher --dapr-grpc-port 50001 --resources-path .\components -- node watch-configuration.mjs

In a separate terminal, change a watched key while the subscriber is running:

docker exec js-config-redis redis-cli SET orderId1 "103||2"

Once the subscription is established, the callback prints orderId1: 103 (version 2). The subscribe promise returns a local handle, not a server-readiness acknowledgment. If you update the store immediately after starting the application, the subscription might not yet be active.

To watch the whole store, replace the subscribeWithKeys call with:

subscription = await client.configuration.subscribe(
  "configstore",
  async (changes) => {
    console.log("Configuration changes:", changes.items);
  },
);

All-key subscription behavior, including whether keys added later are watched, depends on the component. Each subscription has its own handle; stop every subscription during shutdown. No DaprServer, application callback route, or --app-port is required: updates arrive on the client’s gRPC stream.

Next steps

Read the client usage guidance for request metadata, response handling, and important subscription failure and cleanup limitations.

2 - Configuration client usage

Guidance for configuration reads, metadata, subscriptions, client lifetime, and error handling in JavaScript

Configuration is available through DaprClient.configuration. This guide targets @dapr/dapr 3.19.0 or later and describes the implementation details and limitations to account for when using this release.

Client lifetime and configuration

Reuse a long-lived DaprClient for configuration operations rather than creating one for each read or subscription. Configuration requires gRPC; the SDK’s default protocol is HTTP and is not supported for these methods.

import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr";

const client = new DaprClient({
  daprHost: "127.0.0.1",
  daprPort: process.env.DAPR_GRPC_PORT ?? "50001",
  communicationProtocol: CommunicationProtocolEnum.GRPC,
});

Port values are strings. When you omit explicit host and port options, the SDK can resolve the sidecar address from DAPR_GRPC_ENDPOINT. Otherwise, it falls back to the configured host and port, with defaults of 127.0.0.1 and DAPR_GRPC_PORT (or 50001). Explicit host and port options take precedence over endpoint settings.

The API token defaults to DAPR_API_TOKEN; you can also supply daprApiToken in the client options. Request metadata described below is passed to the configuration component and is not a substitute for sidecar authentication.

Stop all subscriptions before calling await client.stop() at application shutdown. Do not rely on client shutdown to explicitly unsubscribe each handle.

Choose an operation

All methods take the configuration component’s name, not its type or YAML file name.

MethodPurposeResult
get(storeName, keys?, metadata?)Read selected keys, or request all keys when keys are omitted or emptyPromise resolving to { items }
subscribe(storeName, callback)Watch the store with an empty key listPromise resolving to a subscription handle
subscribeWithKeys(storeName, keys, callback)Watch selected keysPromise resolving to a subscription handle
subscribeWithMetadata(storeName, keys, metadata, callback)Watch keys with store-specific request metadataPromise resolving to a subscription handle

The gRPC implementation removes empty strings ("") from key arrays. If the resulting array is empty, it sends an all-key request; it does not mean “read or watch nothing.” Use explicit nonempty keys when you need a bounded selection. Support for all-key requests is component-specific.

There are no configuration write or delete methods and no public client.configuration.unsubscribe(...) method. Update the backing store directly and use the returned handle’s stop() to stop a subscription.

Understand response shapes

Both get and subscription callbacks expose an items object indexed by configuration key:

{
  "items": {
    "orderId1": {
      "key": "orderId1",
      "value": "101",
      "version": "1",
      "metadata": {}
    }
  }
}

Each item’s key repeats its map key. value and version are strings; versions are store-specific and may be empty. metadata contains the response metadata supplied by the component.

The SDK does not deserialize values into numbers, booleans, or application objects. Parse and validate them in application code. For example, a JSON setting requires JSON.parse(item.value), with appropriate error handling.

Handle missing keys explicitly. A successful response can have an empty items object; do not assume a missing key causes the read to reject.

Subscription callbacks receive the items in each update, not a guaranteed full-store snapshot. Empty-item stream messages are skipped. The SDK keeps the runtime subscription ID internally and does not expose it in the handle or callback response.

Pass request metadata

get accepts metadata as its third argument. subscribeWithMetadata accepts metadata before the callback. Use the component’s documented metadata keys and string values, without an HTTP metadata. prefix.

For example, after configuring an Azure App Configuration component named appconfig, select a label:

const config = await client.configuration.get(
  "appconfig",
  ["application:theme"],
  { label: "production" },
);
console.log(config.items);

const subscription = await client.configuration.subscribeWithMetadata(
  "appconfig",
  ["application:theme"],
  { label: "production" },
  async (changes) => {
    console.log("Updated settings:", changes.items);
  },
);

// Later, during shutdown:
await subscription.stop();

This snippet assumes an existing gRPC client. Pass [] as the keys argument to request an all-key subscription with metadata, subject to component support.

Metadata is passed through to the store. It is not automatically copied into each returned item’s metadata. Supported fields, filtering, polling intervals, and change-notification behavior depend on the component; Redis, for example, does not implement the Azure label selector.

Subscription lifetime and callbacks

Each subscribe call starts an independent background stream and returns a handle with stop(). Multiple subscriptions can share one client, including subscriptions for the same keys. Retain and stop each handle separately.

Provide an async callback, or a function returning Promise<void>. The SDK awaits each callback before consuming the next message on that subscription. Keep callbacks short; a slow callback delays subsequent updates. Handle processing failures explicitly inside the callback and report them through your application’s logging or monitoring.

Do not assume that subscribing immediately provides current values. Initial-value delivery depends on the runtime and component, and the SDK does not perform an initial get for you. Read required values with get and reconcile subsequent updates according to your application’s consistency needs. Separate reads and subscriptions are not an atomic snapshot-and-watch operation.

Awaiting a subscribe method only returns the local handle; it does not wait for a server acknowledgment or first update. There is no readiness promise, completion promise, or error callback on the handle.

Stop a subscription

Use the handle returned by the subscribe method:

await subscription.stop();

In the reviewed versions, stop() aborts the local stream and, if a runtime subscription ID has arrived, attempts an explicit unsubscribe request. The implementation is asynchronous even though the published SubscribeConfigurationStream type declares stop: () => void; using await waits for that implementation’s cleanup.

Cleanup is best-effort: unsubscribe failures are suppressed, and stopping before an ID arrives cannot issue an ID-based unsubscribe. A resolved stop() is not confirmation of server-side removal. Stopping also does not wait for an already-running callback to finish or cancel application work started by that callback.

The configuration methods do not accept an AbortSignal or a per-call timeout option. Use stop() to cancel a subscription; do not pass binary-store-style cancellation options to configuration methods.

Error handling and recovery

Await reads and subscription creation at your application’s error boundary. A failed get rejects, for example when the component is missing, the store cannot be reached, or the sidecar returns an error. gRPC runtime errors use the Connect transport’s error codes rather than HTTP status codes. Calling configuration methods on an HTTP client rejects with HTTPNotSupportedError.

Report callback errors within the callback itself. For applications that require fresh settings, add independent freshness checks or periodic reads and an application-level recovery policy; do not treat possession of a subscription handle, or a lack of updates, as proof that the watch is healthy. If you recreate subscriptions, stop the previous handles and reconcile current values.

See the configuration how-to guide for a complete read-and-watch example and the Configuration API reference for runtime semantics.