Dapr Secrets JavaScript SDK
Retrieve individual and bulk secrets using the Dapr JavaScript SDK
Use DaprClient.secret to retrieve secrets from a configured Dapr secret store in JavaScript or TypeScript. The SDK provides single-secret and bulk retrieval over HTTP and gRPC. It does not provide methods to create, update, delete, or watch secrets.
These guides target @dapr/dapr 3.19.0 or later. Secrets use the stable Dapr Secrets API; this is not a new building block introduced in SDK 3.19.
Follow the how-to guide for local setup and retrieval. Read the client usage guidance for configuration, response shapes, metadata, protocol limitations, and errors.
HTTP and gRPC differences
In SDK 3.19.0, the gRPC single-secret implementation ignores metadata and returns only the first key-value pair from a multi-valued secret. Use HTTP when you need per-request metadata or all values from an individual secret. Bulk retrieval preserves nested values with either protocol, but neither protocol accepts bulk metadata through the SDK.See the Secrets building block overview and supported secret stores for runtime capabilities and component-specific requirements.
1 - How to: Retrieve secrets with the Dapr JavaScript SDK
Configure a secret store and retrieve individual and bulk secrets with the Dapr JavaScript SDK
This guide uses DaprClient.secret to retrieve a single secret and all accessible secrets from a local secret store. The examples use JavaScript ES modules; the same methods are available in TypeScript.
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 explicit version ensures that npm installs the 3.19.0 release used in this guide. You can substitute a later version.
Create a file named secrets.json in your application’s working directory:
{
"api-key": "development-api-key",
"database": {
"username": "development-user",
"password": "development-password"
}
}
Create a components directory and save the following as components\local-secret-store.yaml:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: local-secret-store
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: secrets.json
- name: multiValued
value: "true"
The component’s metadata.name, local-secret-store, is the store name passed to the SDK. The secretsFile path is relative to the directory where you run dapr run, not the component file. Setting multiValued to "true" lets the database secret contain both username and password.
Development only
The local file component reads plaintext secrets without authentication. Use only dummy values for this example, do not commit real secrets, and configure a production
secret store for deployed applications.
See the local file secret store reference for flattening and multi-valued secret behavior.
Retrieve individual and bulk secrets
Save the following as app.mjs. Using .mjs lets Node.js run the ES module without changing your project’s package configuration.
import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr";
const client = new DaprClient({
daprHost: "127.0.0.1",
daprPort: "3500",
communicationProtocol: CommunicationProtocolEnum.HTTP,
});
const storeName = "local-secret-store";
try {
const apiKey = await client.secret.get(storeName, "api-key");
if (typeof apiKey["api-key"] !== "string") {
throw new Error("The required api-key secret was not returned");
}
const database = await client.secret.get(storeName, "database");
if (
typeof database.username !== "string" ||
typeof database.password !== "string"
) {
throw new Error("The required database credentials were not returned");
}
const secrets = await client.secret.getBulk(storeName);
if (
typeof secrets.database?.username !== "string" ||
typeof secrets.database?.password !== "string"
) {
throw new Error("Bulk retrieval did not return the database credentials");
}
console.log("Retrieved the required secrets without logging their values");
} finally {
await client.stop();
}
Start the application and its sidecar from the directory containing secrets.json:
dapr run --app-id secrets-example --dapr-http-port 3500 --dapr-grpc-port 50001 --resources-path .\components -- node app.mjs
This is a client-only application; it does not need a DaprServer, an application port, or an inbound secrets handler.
Single-secret responses
get(storeName, key) returns the secret’s key-value data directly. For a name/value secret, the only key is the secret name itself; for a multi-valued secret, the keys are its fields and the secret name is not included. With the sample data, get(storeName, "api-key") returns:
{
"api-key": "development-api-key"
}
Over HTTP, get(storeName, "database") returns both values:
{
"username": "development-user",
"password": "development-password"
}
Bulk responses
getBulk(storeName) returns an object keyed by secret name, with a nested object containing each secret’s data:
{
"api-key": {
"api-key": "development-api-key"
},
"database": {
"username": "development-user",
"password": "development-password"
}
}
Bulk retrieval is not an array or a paginated iterator. It loads the full response into memory. Use it only when the application needs all accessible secrets and the component supports bulk retrieval.
Use gRPC
Configure CommunicationProtocolEnum.GRPC and the sidecar’s gRPC port, "50001", instead of HTTP and "3500". The same get and getBulk methods are available.
Multi-valued single-secret limitation
In SDK 3.19.0, gRPC get returns only the first entry from the runtime’s secret data map. It never returns both database fields in this example, so changing only the protocol causes the database validation to fail. Keep HTTP for individual multi-valued secrets. gRPC getBulk preserves all nested values, but retrieves all accessible secrets rather than just one.The third argument to get is a query-string string, not a metadata object. Include the metadata. prefix on each parameter and omit the leading ?. Use URLSearchParams to encode values.
For example, with an AWS Secrets Manager component named aws-secrets, retrieve a particular version:
const metadata = new URLSearchParams({
"metadata.version_id": "your-version-id",
}).toString();
const secret = await client.secret.get(
"aws-secrets",
"database-password",
metadata,
);
This snippet assumes an existing HTTP client and a separately configured AWS component; it is not needed for the local file example. Supported metadata depends on the secret store component. For example, Kubernetes supports metadata.namespace, while some cloud stores support version selection.
The gRPC implementation ignores this argument in SDK 3.19.0. getBulk has no metadata argument with either protocol. See the client usage guidance for the complete API surface and error behavior.
2 - Secrets client usage
Configure the JavaScript secrets client and understand metadata, response shapes, protocol differences, and errors
Secrets are available through DaprClient.secret; there is no separate secrets client package or builder. This guidance targets @dapr/dapr 3.19.0 or later.
Client lifetime and configuration
Reuse a long-lived DaprClient across operations. Its secrets client shares the parent client’s connection and authentication settings. Await outstanding operations before calling await client.stop() during application shutdown; do not stop a shared client after each secret lookup.
HTTP is the default protocol. To explicitly configure gRPC:
import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr";
const client = new DaprClient({
daprHost: "127.0.0.1",
daprPort: "50001",
communicationProtocol: CommunicationProtocolEnum.GRPC,
daprApiToken: process.env.DAPR_API_TOKEN,
});
Use CommunicationProtocolEnum.HTTP with the HTTP port (default "3500") for HTTP. Port values are strings. Selecting the SDK protocol controls client-to-sidecar communication; it does not require an application server.
If neither daprHost nor daprPort is supplied, the SDK can use DAPR_HTTP_ENDPOINT or DAPR_GRPC_ENDPOINT for the selected protocol. Explicit host or port options take precedence over the endpoint environment variable. Otherwise, the SDK defaults to 127.0.0.1 and DAPR_HTTP_PORT or DAPR_GRPC_PORT, falling back to 3500 or 50001. The token defaults to DAPR_API_TOKEN when daprApiToken is omitted.
The API token authenticates to the Dapr sidecar, not directly to the secret store provider. Configure provider credentials and access in the secret store component. Use secret scopes to restrict which secrets an application can retrieve, and component scopes to restrict which applications can use a component.
Available operations
The public signatures are:
get(secretStoreName: string, key: string, metadata?: string): Promise<object>;
getBulk(secretStoreName: string): Promise<object>;
Call these as client.secret.get(...) and client.secret.getBulk(...).
| Capability | HTTP | gRPC in 3.19.0 |
|---|
| Retrieve a single secret | Returns the full data map | Returns only the first key-value pair, or {} for an empty data map |
Per-request metadata on get | Query string with metadata.-prefixed keys | Argument is accepted but ignored |
| Bulk retrieval | Nested secret data maps | Nested secret data maps, preserving all values |
Per-request metadata on getBulk | Not exposed | Not exposed |
These limitations are present in both the 3.19.0 HTTP implementation and gRPC implementation, and remain in the latest source reviewed for this guide. Check the implementation and release notes when upgrading.
The SDK does not expose secret writes, deletes, subscriptions, typed secret generation, or automatic secret refresh. Manage secret creation and rotation in the underlying provider. If your application caches retrieved values, it must decide when to fetch them again.
HTTP get appends the metadata string directly to the request URL after ?. Do not include a leading ?, pass an object, or omit the metadata. prefixes. For example:
const metadata = new URLSearchParams({
"metadata.namespace": "payments",
}).toString();
const secret = await client.secret.get(
"kubernetes",
"database-credentials",
metadata,
);
This requires a Kubernetes secret store and an existing HTTP client. Metadata keys are component-specific; see the Secrets API reference and component reference.
Store names and secret names are case-sensitive. The HTTP implementation interpolates them into URL paths without encoding them. If a secret name contains URL-reserved characters, encode the secret name with encodeURIComponent before passing it to HTTP get. Do not encode gRPC secret names, which are sent as protobuf fields.
Response shapes and TypeScript
For a successful single-secret request, the data is a map of string keys to string values. Some stores return the secret name as the only key; others, such as Kubernetes, return several fields without the secret name as a wrapper. HTTP preserves all these fields. Do not rely on gRPC’s first-entry selection to choose a particular field from a multi-valued secret.
A successful bulk response is a map from secret names to their data maps. It is not a flat map, and the field names inside each secret are determined by the component. Bulk availability and the returned secrets depend on the component and access configuration.
Both methods are declared as Promise<object>, not as generic or strongly typed dictionaries. TypeScript callers should validate a response before indexing it, rather than assuming the declaration guarantees string-valued fields:
function isStringMap(value: unknown): value is Record<string, string> {
return (
typeof value === "object" &&
value !== null &&
!Array.isArray(value) &&
Object.values(value).every((entry) => typeof entry === "string")
);
}
const secret = await client.secret.get("local-secret-store", "api-key");
if (!isStringMap(secret) || typeof secret["api-key"] !== "string") {
throw new Error("The required api-key secret was not returned");
}
const apiKey = secret["api-key"];
This snippet assumes an existing client and runs inside an async function or an ES module. For bulk responses, first validate the outer object, then validate the data map for each secret your application requires. Checking only that the response is an object does not guarantee that a required secret exists.
Errors and missing secrets
Await each operation and handle failures at your application’s error boundary. Missing components, denied access, unsupported bulk retrieval, provider failures, and sidecar connection failures can reject the promise.
- HTTP runtime errors: Non-success responses become JavaScript
Error instances whose message is a JSON string containing status (HTTP status), error (status text), and error_msg (runtime response body). Network and startup errors can have different shapes; do not assume every error message is JSON. - gRPC runtime errors: RPC failures use
ConnectError from @connectrpc/connect, with a gRPC/Connect error code rather than an HTTP status. - Missing or empty secrets: Do not assume a missing secret always rejects. Behavior depends on the component and runtime response. HTTP
204 No Content resolves to an empty string in this SDK, despite the Promise<object> declaration. gRPC resolves an empty data map to {}. Validate required fields and report their absence explicitly.
See the Secrets API reference for runtime status codes. Avoid logging returned secrets or raw error payloads that might contain sensitive provider information.
Cancellation and bulk retrieval limits
Neither method exposes an AbortSignal, per-call timeout, or request options argument. Do not assume cancellation support from another SDK building block also applies to secrets. Racing a request against a timer only limits how long your application waits; it does not cancel the underlying request.
getBulk returns the entire response in memory, without pagination, streaming, or a key filter. Prefer individual requests when you need only a few secrets. Using bulk retrieval to work around the gRPC multi-value limitation may retrieve more secrets than your application needs; HTTP get is usually the narrower choice.
Testing your application
Keep secret access behind an application-owned interface or inject the methods your code needs. Test single-value and multi-valued responses, nested bulk responses, missing required fields, and rejected promises. Use the actual object return type rather than assuming a typed secret model.
For integration tests, run a sidecar with a development secret store such as the local file example. Test HTTP and gRPC separately if your application supports both; a single-value success case does not exercise metadata or multi-valued response differences.