Home / Blog / Connect to Camunda 8 from Node.js with a typed REST client

Connect to Camunda 8 from Node.js with a typed REST client

Camunda 8’s REST API is large — deployment, process instances, user tasks, incidents, decisions, and more. @bpmnkit/api wraps it in a single typed client: 180 methods across 30+ resource classes, generated from Camunda’s own OpenAPI specification, so every request and response is typed.

Connecting

import { CamundaClient } from "@bpmnkit/api";

const client = new CamundaClient({
  // The cluster's REST address with /v2: ZEEBE_REST_ADDRESS in the credentials file
  // Camunda Hub gives you when you create an API client for a cluster
  baseUrl: `${process.env.ZEEBE_REST_ADDRESS}/v2`,
  auth: {
    type: "oauth2",
    clientId: process.env.ZEEBE_CLIENT_ID ?? "",
    clientSecret: process.env.ZEEBE_CLIENT_SECRET ?? "",
    tokenUrl: process.env.ZEEBE_AUTHORIZATION_SERVER_URL ?? "",
    audience: "zeebe.camunda.io",
  },
});

OAuth2, Bearer, and Basic auth are all supported. Under the hood, the client caches its OAuth2 token until shortly before it expires, can cache read responses (LRU + TTL) when you turn that on, and retries transient failures with exponential backoff, so a flaky network blip during a deploy doesn’t fail the whole request.

Deploying and starting a process

// Deploy a process definition: a multipart upload, one `resources` part per file
const form = new FormData();
form.append("resources", new Blob([xml]), "my-flow.bpmn");
await client.resource.createDeployment(form);

// Start a new instance
const instance = await client.processInstance.createProcessInstance({
  processDefinitionId: "my-flow",
  variables: { orderId: "ord-123" },
});

xml here can come from anywhere — including a diagram generated with @bpmnkit/core in the same process, so a build step can generate, simulate, and deploy a process without leaving TypeScript.

Observing what happens

The client is a TypedEventEmitter, so you can hook into the request lifecycle without wrapping every call yourself:

client.on("request", (e) => console.log(e.method, e.url));
client.on("error", (e) => metrics.inc("api.error"));

That’s useful for logging, metrics, or feeding a dashboard — you get visibility into every call the client makes against Camunda 8 without threading logging through each call site.

Where to go next