Skip to main content

Observra Node.js SDK

observra-sdk-node routes your application's LLM calls through the Observra Gateway and traces them, so every request appears in your dashboard with its model, tokens, cost, latency, and errors — without changing how you call the model.

:::info Version These pages describe 0.2.0. Options added in that release are marked where they appear — on an older install they are accepted and then ignored, since the header or behaviour behind them does not exist yet. :::

The whole integration

Two lines added to an application that already calls an LLM:

import * as observra from "observra-sdk-node";
import Groq from "groq-sdk";

observra.configure({ serviceName: "my-app" });
await observra.instrument();

// Unchanged. Stock groq-sdk, straight from Groq's own docs.
const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });
const res = await groq.chat.completions.create({
model: "llama-3.3-70b-versatile",
messages: [{ role: "user", content: "Hello" }],
});

configure() reads your gateway key from OBSERVRA_GATEWAY_KEY and points at Observra's hosted gateway. instrument() patches the global fetch, so a request that would have gone to api.groq.com goes to your gateway instead, with credentials remapped correctly.

That works for 15 provider SDKs and for any agent framework built on them.

What you get

  • A gateway observation per call — provider, model, input/output tokens, cost, latency, status
  • A trace id shared by every call in one logical operation, so a multi-step agent run reads as a single flow instead of unrelated rows
  • A span describing the call, correlated to that observation
  • Optional guardrails that scan prompts and responses for PII and secrets before they leave your process

None of it can break your application. Telemetry failures are swallowed by design: if tracing fails, your LLM call still runs.

The other integration path

Patching the global fetch isn't acceptable everywhere. If that's you, the SDK also ships its own client for every provider the gateway supports:

import * as observra from "observra-sdk-node";

observra.configure({ serviceName: "my-app" });

// No instrument(), no fetch patching - this client talks to the gateway directly.
const groq = new observra.Groq({ apiKey: process.env.GROQ_API_KEY });
const res = await groq.chat.completions.create({
model: "llama-3.3-70b-versatile",
messages: [{ role: "user", content: "Hello" }],
});

The trade-off is coverage: these clients handle chat completions and streaming, not every endpoint a provider's own SDK exposes (transcription, batches, files).

Which to choose

Keep your own SDKUse SDK clients
Code changesTwo lines, onceSwap each client constructor
Provider API coverageEverything your SDK doesChat completions + streaming
Patches global fetchYesNo
Agent frameworksWorksNot applicable
Response typesYour SDK's ownunknown — you cast

Keep your own SDK is the better default. Reach for the SDK's clients when patching fetch is unacceptable in your environment, or when you want provider access without adding a provider SDK dependency.

Requirements

  • Node 18 or later — the SDK uses the global fetch
  • ESM only"type": "module", or import from an .mjs file. There is no CommonJS build; from CJS, use await import("observra-sdk-node")
  • @opentelemetry/api as a peer dependency — a peer rather than a direct dependency so an application already running OpenTelemetry shares one instance. Two copies silently break context propagation
npm i observra-sdk-node @opentelemetry/api

What you need before starting

One credential: a gateway key from the Observra Dashboard under Gateway Keys. It starts with obs_ and identifies which Application and Environment your calls belong to.

It is not a provider API key. You keep sending your provider key yourself; Observra forwards it to the provider and never stores it.

Set it as OBSERVRA_GATEWAY_KEY and configure() picks it up with no arguments.

Next