How to: Call services with the JavaScript SDK

Invoke application methods with HTTP or gRPC clients, headers, and query parameters

DaprClient.invoker.invoke() sends a request through the caller’s sidecar to a target application. These examples use @dapr/dapr 3.19.0 or later. Follow the receiving invocations guide to start the invocation-service target used below.

Invoke an application over HTTP

Save this complete example as client.mjs:

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

async function main() {
  const client = new DaprClient({
    daprHost: "127.0.0.1",
    daprPort: "3501",
  });

  try {
    const response = await client.invoker.invoke(
      "invocation-service",
      "orders",
      HttpMethod.POST,
      { orderId: "101", quantity: 2 },
      { headers: { "X-Correlation-ID": "order-101" } },
    );
    console.log(response);
  } finally {
    await client.stop();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

In another terminal, run the caller with its own sidecar:

dapr run --app-id invocation-caller --dapr-http-port 3501 --dapr-grpc-port 50002 -- node client.mjs

The caller does not need --app-port because it does not receive callbacks. Its sidecar ports differ from those of the target service.

Invocation arguments

The public signature is:

invoke(
  appId: string,
  methodName: string,
  method: HttpMethod,
  data?: object,
  options?: InvokerOptions,
): Promise<object>;
ArgumentMeaning
appIdTarget Dapr app ID, not the caller’s app ID or host address.
methodNameTarget method or relative HTTP path, for example orders or orders/101. Use no leading slash.
methodHttpMethod.GET, POST, PUT, PATCH, or DELETE. Supply it explicitly; the implementations default to GET, but the public TypeScript interface requires it.
dataOptional payload. Objects and arrays use JSON by default.
optionsAn InvokerOptions object containing optional headers. It has no per-call timeout, cancellation, or retry fields.

The result is the decoded response body, not a response envelope. Although the public return type is object, HTTP may return text and a serializer may return other JavaScript values. There is no invoke<T>() overload and TypeScript types do not validate the response at runtime.

Select a verb

These snippets assume an existing client and matching routes on the target:

const order = await client.invoker.invoke("invocation-service", "orders/101", HttpMethod.GET);

const replaced = await client.invoker.invoke(
  "invocation-service", "orders/101", HttpMethod.PUT, { quantity: 4 },
);

const updated = await client.invoker.invoke(
  "invocation-service", "orders/101", HttpMethod.PATCH, { quantity: 3 },
);

await client.invoker.invoke("invocation-service", "orders/101", HttpMethod.DELETE);

Do not provide a body with an HTTP GET request. A gRPC invocation with omitted data sends the default payload {}, rather than an absent body.

Pass headers and query parameters over HTTP

Use the fifth argument for headers. To omit the body, pass undefined as the fourth argument:

const query = new URLSearchParams({ status: "pending", customer: "Ada Lovelace" });
const response = await client.invoker.invoke(
  "invocation-service",
  `orders?${query.toString()}`,
  HttpMethod.GET,
  undefined,
  { headers: { "X-Correlation-ID": "query-101" } },
);

Encode individual dynamic path segments with encodeURIComponent() and query values with URLSearchParams. The SDK interpolates methodName into the HTTP invocation URL; it does not encode it for you.

Custom headers are supported by the HTTP implementation. The gRPC implementation does not forward options.headers as request metadata. With a configured serializer, it only uses headers["Content-Type"] to override the payload content type. It also does not populate the request’s separate gRPC HTTP-extension query-string field, so do not use the HTTP query-string example with a gRPC client.

Invoke through a gRPC sidecar endpoint

Replace the client construction in client.mjs with:

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

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

Keep the HttpMethod import and invoke() call from the original example, but omit the HTTP headers option. Use the same caller startup command: it exposes both sidecar endpoints. The receiving application can remain HTTP.

By default, the gRPC client decodes the response bytes as UTF-8 and parses them as JSON. An empty body or plain-text response rejects with COULD_NOT_PARSE_RESULT, even if the remote operation succeeded. Return a JSON value from the target, or configure a compatible serializer.

Call across namespaces

Where supported by Dapr’s name-resolution configuration, qualify the target app ID with its namespace:

const response = await client.invoker.invoke(
  "invocation-service.production", "orders", HttpMethod.POST, { orderId: "101" },
);

Namespace routing is a runtime capability, not a separate SDK option. See service invocation across namespaces.

Call a non-Dapr HTTP endpoint

With an HTTP client, the same API can target an HTTPEndpoint resource by name or a fully qualified URL, when supported by the runtime:

const configuredEndpoint = await client.invoker.invoke(
  "external-orders", "orders/101", HttpMethod.GET,
);

const urlEndpoint = await client.invoker.invoke(
  "https://api.example.com", "orders/101", HttpMethod.GET,
);

These are alternative examples: configure external-orders or replace the illustrative URL before running them. The target does not need a Dapr sidecar. See invoking non-Dapr endpoints for resource configuration, authentication, and runtime requirements.