Client

Introduction

The Dapr Client allows you to communicate with the Dapr Sidecar and get access to its client facing features such as Publishing Events, Invoking Output Bindings, State Management, Secret Management, and much more.

For LLM interactions, use client.conversation.converse(request). The Conversation guides cover setup, message history, structured output, caching, token usage, and tool calling with JavaScript SDK 3.19.0 or later.

Pre-requisites

Installing and importing Dapr’s JS SDK

  1. Install the SDK with npm:
npm i @dapr/dapr --save
  1. Import the libraries:
import { DaprClient, DaprServer, HttpMethod, CommunicationProtocolEnum } from "@dapr/dapr";

const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server

// HTTP Example
const client = new DaprClient({ daprHost, daprPort });

// GRPC Example
const client = new DaprClient({ daprHost, daprPort, communicationProtocol: CommunicationProtocolEnum.GRPC });

Running

To run the examples, you can use two different protocols to interact with the Dapr sidecar: HTTP (default) or gRPC.

Using HTTP (default)

import { DaprClient } from "@dapr/dapr";
const client = new DaprClient({ daprHost, daprPort });
# Using dapr run
dapr run --app-id example-sdk --app-protocol http -- npm run start

# or, using npm script
npm run start:dapr-http

Using gRPC

Since HTTP is the default, you will have to adapt the communication protocol to use gRPC. You can do this by passing an extra argument to the client or server constructor.

import { DaprClient, CommunicationProtocolEnum } from "@dapr/dapr";
const client = new DaprClient({ daprHost, daprPort, communicationProtocol: CommunicationProtocolEnum.GRPC });
# Using dapr run
dapr run --app-id example-sdk --app-protocol grpc -- npm run start

# or, using npm script
npm run start:dapr-grpc

Environment Variables

Dapr Sidecar Endpoints

You can use the DAPR_HTTP_ENDPOINT and DAPR_GRPC_ENDPOINT environment variables to set the Dapr Sidecar’s HTTP and gRPC endpoints respectively. When these variables are set, the daprHost and daprPort don’t have to be set in the options argument of the constructor, the client will parse them automatically out of the provided endpoints.

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

// Using HTTP, when DAPR_HTTP_ENDPOINT is set
const client = new DaprClient();

// Using gRPC, when DAPR_GRPC_ENDPOINT is set
const client = new DaprClient({ communicationProtocol: CommunicationProtocolEnum.GRPC });

If the environment variables are set, but daprHost and daprPort values are passed to the constructor, the latter will take precedence over the environment variables.

Dapr API Token

You can use the DAPR_API_TOKEN environment variable to set the Dapr API token. When this variable is set, the daprApiToken doesn’t have to be set in the options argument of the constructor, the client will get it automatically.

General

Increasing Body Size

You can increase the body size that is used by the application to communicate with the sidecar by using aDaprClient’s option.

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

// Allow a body size of 10Mb to be used
// The default is 4Mb
const client = new DaprClient({
  daprHost,
  daprPort,
  communicationProtocol: CommunicationProtocolEnum.HTTP,
  maxBodySizeMb: 10,
});

Proxying Requests

In JavaScript SDK 3.19.0, client.proxy.create() is unsupported for both HTTP and the gRPC ConnectRPC transport and rejects when called. Use client.invoker.invoke() for Dapr service invocation.

For native protobuf services, use your generated gRPC client connected to the Dapr sidecar with dapr-app-id metadata. See native gRPC services and streaming and the runtime gRPC proxying guide.

Building blocks

The JavaScript Client SDK allows you to interface with all of the Dapr building blocks focusing on Client to Sidecar features.

Search API

Use client.search to manage indexes and index, retrieve, delete, and query JSON documents over HTTP or gRPC. Requires Dapr runtime 1.19 or later and JavaScript SDK 3.19.0 or later.

See the Search how-to guide for a complete example, and Search client usage for method signatures, query options, write acknowledgements, cancellation, and errors.

Jobs API

Use client.jobs to schedule, retrieve, and delete jobs with JavaScript SDK 3.19.0 or later. See the Jobs how-to guide and client usage guidance for scheduling options, payloads, runtime compatibility, and error handling.

Invocation API

For JavaScript SDK 3.19.0 or later, see the service invocation overview for HTTP and gRPC capabilities.

Invoke a Service

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

const daprHost = "127.0.0.1";
const daprPort = "3500";

async function start() {
  const client = new DaprClient({ daprHost, daprPort });

  const serviceAppId = "my-app-id";
  const serviceMethod = "say-hello";

  try {
    const response = await client.invoker.invoke(
      serviceAppId,
      serviceMethod,
      HttpMethod.POST,
      { hello: "world" },
      { headers: { "X-User-ID": "123" } },
    );
    console.log(response);
  } finally {
    await client.stop();
  }
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

Follow calling services for a runnable client and server setup, other verbs, query parameters, namespaces, and non-Dapr endpoints. See configuration and usage for serialization and errors. Custom request headers are forwarded by the HTTP invoker, not the gRPC invoker.

State Management API

Use client.state to access configured state stores over HTTP or gRPC. The state management guides cover JavaScript SDK 3.19.0 or later, including bulk deletion, ETags, state options, TTL, transactions, serialization, and protocol-specific errors.

Save, Get and Delete application state

Follow How to: Manage state for a runnable example with component configuration, save/get/delete, bulk reads and deletes, conditional updates, and transactions.

HTTP save, delete, and deleteBulk can return { error } instead of rejecting. Check that field and also handle promise rejections; gRPC RPC failures reject. See state client usage for the complete method and response reference.

Query State API

The alpha query API supports filtering, sorting, and pagination on query-capable stores over HTTP and gRPC. Follow How to: Query state for query expressions, continuation tokens, and result decoding.

PubSub API

For JavaScript SDK 3.19.0 or later, see the publish and subscribe overview to choose a delivery mode.

Publish messages

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

const daprHost = "127.0.0.1";
const daprPort = "3500";

async function start() {
  const client = new DaprClient({ daprHost, daprPort });

  try {
    const response = await client.pubsub.publish("pubsub", "orders", { orderId: "101" });
    if (response.error) {
      throw response.error;
    }
  } finally {
    await client.stop();
  }
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

See publishing events for bulk operations, metadata, CloudEvents, raw payloads, and transport-specific error handling. See configuration and usage for client lifetime and serialization.

Streaming message subscriptions

With a gRPC client, client.pubsub.subscribeStream() receives messages without DaprServer or an application callback port. Follow the streaming subscription guide for a complete worker, runtime requirements, acknowledgements, cancellation, and failure supervision.

Bindings API

Invoke Output Binding

Output Bindings

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

const daprHost = "127.0.0.1";
const daprPort = "3500";

async function start() {
  const client = new DaprClient({ daprHost, daprPort });

  const bindingName = "my-binding-name";
  const bindingOperation = "create";
  const message = { hello: "world" };

  const response = await client.binding.send(bindingName, bindingOperation, message);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

For a full guide on output bindings visit How-To: Use bindings.

Secret API

Retrieve secrets

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

const daprHost = "127.0.0.1";
const daprPort = "3500";

async function start() {
  const client = new DaprClient({ daprHost, daprPort });

  const secretStoreName = "my-secret-store";
  const secretKey = "secret-key";

  // Retrieve a single secret; the result is the secret's key-value data
  const secret = await client.secret.get(secretStoreName, secretKey);

  // Retrieve all accessible secrets, nested by secret name
  const secrets = await client.secret.getBulk(secretStoreName);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

For JavaScript SDK 3.19.0 or later, follow the Secrets how-to guide for a runnable example, or read the Secrets client usage guidance for configuration, TypeScript response validation, and error handling.

In 3.19.0, HTTP get accepts metadata as a query-string string and preserves all values in a multi-valued secret. gRPC get ignores metadata and returns only the first key-value pair. Bulk retrieval preserves nested values with either protocol, but neither exposes bulk metadata.

For the underlying building block, see How to: Retrieve a secret.

Configuration API

Use client.configuration to read application settings and subscribe to changes. The JavaScript SDK requires gRPC for every configuration method; the HTTP client is not supported.

The configuration how-to guide provides runnable examples for @dapr/dapr 3.19.0 or later. The client usage guidance covers all methods, metadata, response shapes, and subscription error limitations.

Get Configuration Keys

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

const daprHost = "127.0.0.1";

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

  try {
    const config = await client.configuration.get("config-store", ["key1", "key2"]);
    console.log(config);
  } finally {
    await client.stop();
  }
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

Sample output:

{
   items: {
     key1: { key: 'key1', value: 'foo', version: '', metadata: {} },
     key2: { key: 'key2', value: 'bar2', version: '', metadata: {} }
   }
}

Subscribe to Configuration Updates

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

const daprHost = "127.0.0.1";

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

  try {
    const stream = await client.configuration.subscribeWithKeys("config-store", ["key1", "key2"], async (data) => {
      console.log("Subscribe received updates from config store: ", data);
    });

    try {
      await new Promise((resolve) => setTimeout(resolve, 60000));
    } finally {
      await stream.stop();
    }
  } finally {
    await client.stop();
  }
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

Sample output:

Subscribe received updates from config store:  {
  items: { key2: { key: 'key2', value: 'bar', version: '', metadata: {} } }
}
Subscribe received updates from config store:  {
  items: { key1: { key: 'key1', value: 'foobar', version: '', metadata: {} } }
}

To watch all keys, use subscribe(storeName, callback). To include store-specific metadata, use subscribeWithMetadata(storeName, keys, metadata, callback). All-key behavior depends on the component. See subscription lifetime and callbacks before relying on a subscription for configuration freshness.

Cryptography API

In @dapr/dapr 3.19.0 or later, client.crypto supports encryption and decryption over both HTTP and gRPC. Pass data and options to receive a Buffer, or pass only options to receive a Node.js Duplex stream.

Prefer gRPC for large payloads and production streaming. Dapr buffers HTTP cryptography output until the request is fully consumed.

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

async function start() {
  const client = new DaprClient({
    daprHost: "127.0.0.1",
    daprPort: "50001",
    communicationProtocol: CommunicationProtocolEnum.GRPC,
  });

  try {
    const ciphertext = await client.crypto.encrypt("A message to encrypt", {
      componentName: "crypto-local",
      keyName: "symmetric256",
      keyWrapAlgorithm: "A256KW",
    });
    const plaintext = await client.crypto.decrypt(ciphertext, {
      componentName: "crypto-local",
    });
    console.log("Decrypted bytes:", plaintext.length);
  } finally {
    await client.stop();
  }
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

This example requires a configured crypto-local component and a compatible symmetric256 key. Follow the JavaScript Cryptography how-to guide for complete setup and file-stream examples. See the client usage guidance for ciphers, key references, errors, and runtime limitations.

Distributed Lock API

Try Lock and Unlock APIs

import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr";
import { LockStatus } from "@dapr/dapr/types/lock/UnlockResponse";

const daprHost = "127.0.0.1";
const daprPortDefault = "3500";

async function start() {
  const client = new DaprClient({ daprHost, daprPort });

  const storeName = "redislock";
  const resourceId = "resourceId";
  const lockOwner = "owner1";
  let expiryInSeconds = 1000;

  console.log(`Acquiring lock on ${storeName}, ${resourceId} as owner: ${lockOwner}`);
  const lockResponse = await client.lock.lock(storeName, resourceId, lockOwner, expiryInSeconds);
  console.log(lockResponse);

  console.log(`Unlocking on ${storeName}, ${resourceId} as owner: ${lockOwner}`);
  const unlockResponse = await client.lock.unlock(storeName, resourceId, lockOwner);
  console.log("Unlock API response: " + getResponseStatus(unlockResponse.status));
}

function getResponseStatus(status: LockStatus) {
  switch (status) {
    case LockStatus.Success:
      return "Success";
    case LockStatus.LockDoesNotExist:
      return "LockDoesNotExist";
    case LockStatus.LockBelongsToOthers:
      return "LockBelongsToOthers";
    default:
      return "InternalError";
  }
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

For a full guide on distributed locks visit How-To: Use Distributed Locks.

Workflow API

DaprClient.workflow exposes the basic workflow management API over HTTP or gRPC. For @dapr/dapr 3.19.0 or later, use scheduleNewWorkflow, getWorkflowState, and raiseEvent; the older start, get, and raise names are deprecated aliases.

For authoring workers, durable waits, listing, history retrieval, reruns, and binary-store payload references, use the separate workflow SDK guides. DaprWorkflowClient uses gRPC and returns WorkflowState, not the basic API’s response object shown below.

Workflow management

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

async function start() {
  const client = new DaprClient();

  // Start a new workflow instance
  const instanceId = await client.workflow.scheduleNewWorkflow("OrderProcessingWorkflow", {
    Name: "Paperclips",
    TotalCost: 99.95,
    Quantity: 4,
  });
  console.log(`Started workflow instance ${instanceId}`);

  // Get a workflow instance
  const workflow = await client.workflow.getWorkflowState(instanceId);
  console.log(
    `Workflow ${workflow.workflowName}, created at ${workflow.createdAt.toUTCString()}, has status ${
      workflow.runtimeStatus
    }`,
  );
  console.log(`Additional properties: ${JSON.stringify(workflow.properties)}`);

  // Pause a workflow instance
  await client.workflow.pause(instanceId);
  console.log(`Paused workflow instance ${instanceId}`);

  // Resume a workflow instance
  await client.workflow.resume(instanceId);
  console.log(`Resumed workflow instance ${instanceId}`);

  // Terminate a workflow instance
  await client.workflow.terminate(instanceId);
  console.log(`Terminated workflow instance ${instanceId}`);

  // Purge a workflow instance
  await client.workflow.purge(instanceId);
  console.log(`Purged workflow instance ${instanceId}`);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});