Skip to main content

Node.js Integration

Also available in: Python · PHP · Go

The "gateway base URL" is the address of your Observra Gateway (https://gateway.observra.in) - your app sends requests here instead of directly to OpenAI, Anthropic, or whichever provider you use. The "gateway key" is a per-Application credential you generate in the Observra Dashboard; it authenticates your app to the gateway and is separate from your provider's own API key, which you still keep and send yourself. See Quickstart and Gateway Authentication if you haven't set these up yet. For the OpenAI SDK specifically, also see the dedicated OpenAI SDK page - it's the reference integration for this gateway.

Try it now

Paste in a gateway URL, your gateway key, and a provider key to send a real request straight from this page - no code required yet.

Loading playground...

Environment variables

GATEWAY_URL
GATEWAY_KEY obs_...
OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY / GROQ_API_KEY / OPENROUTER_API_KEY / OLLAMA_API_KEY / AZURE_OPENAI_KEY

Install:

npm install openai @anthropic-ai/sdk @google/genai

Optional: distributed tracing

To send a traceparent header (see Trace Context) install OpenTelemetry:

npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-trace-node
import { NodeSDK } from "@opentelemetry/sdk-node";
import { ConsoleSpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-node";
import { trace, context } from "@opentelemetry/api";

const otelSdk = new NodeSDK({
spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())],
serviceName: "my-service",
});
otelSdk.start();
const tracer = trace.getTracer("my-service");

const rootSpan = tracer.startSpan("my_operation");
const rootCtx = trace.setSpan(context.active(), rootSpan);
const { traceId, spanId } = rootSpan.spanContext();
const traceparent = `00-${traceId}-${spanId}-01`;

// Make the actual gateway call inside context.with(rootCtx, ...) so any
// nested spans you add stay attached to this same trace, then end the span:
await context.with(rootCtx, async () => {
// ... call the gateway here ...
});
rootSpan.end();

Each "With trace" tab below assumes a tracer from this setup is in scope.

Raw HTTP request (fetch)

const res = await fetch(`${process.env.GATEWAY_URL}/openai/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.GATEWAY_KEY}`,
"X-Provider-Key": process.env.OPENAI_API_KEY,
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }],
}),
});
const body = await res.json();
curl -X POST "${GATEWAY_URL}/openai/chat/completions" \
-H "Authorization: Bearer ${GATEWAY_KEY}" \
-H "X-Provider-Key: ${OPENAI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'

Native SDKs

import OpenAI from "openai";

const client = new OpenAI({
apiKey: process.env.GATEWAY_KEY,
baseURL: `${process.env.GATEWAY_URL}/openai`,
defaultHeaders: { "X-Provider-Key": process.env.OPENAI_API_KEY },
});

const completion = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }],
});

Tool calling (native SDK)

import OpenAI from "openai";

const client = new OpenAI({
apiKey: process.env.GATEWAY_KEY,
baseURL: `${process.env.GATEWAY_URL}/openai`,
defaultHeaders: { "X-Provider-Key": process.env.OPENAI_API_KEY },
});

const tools = [{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather for a given city.",
parameters: {
type: "object",
properties: { location: { type: "string" } },
required: ["location"],
},
},
}];

const completion = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "What's the weather in New York?" }],
tools,
});

const toolCall = completion.choices[0].message.tool_calls?.[0];
if (toolCall) {
const { location } = JSON.parse(toolCall.function.arguments);
const result = `It's 31C and partly cloudy in ${location}.`;

const followUp = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{ role: "user", content: "What's the weather in New York?" },
completion.choices[0].message,
{ role: "tool", tool_call_id: toolCall.id, content: result },
],
});
console.log(followUp.choices[0].message.content);
}

Wrap the same call in tracer.startSpan(...) / traceparent as shown above if you want it correlated with a trace.

Agent frameworks & SDKs

More advanced: these frameworks wrap the SDKs above. Each still points its underlying model client at the gateway route.

import { ChatOpenAI } from "@langchain/openai";
import { createReactAgent } from "@langchain/langgraph/prebuilt";

const model = new ChatOpenAI({
model: "gpt-4o-mini",
apiKey: process.env.GATEWAY_KEY,
configuration: {
baseURL: `${process.env.GATEWAY_URL}/openai/v1`,
defaultHeaders: { "X-Provider-Key": process.env.OPENAI_API_KEY },
},
});

const agent = createReactAgent({ llm: model, tools: [] });
const result = await agent.invoke({ messages: [{ role: "user", content: "hello" }] });

With trace, run the agent inside context.with(trace.setSpan(context.active(), rootSpan), () => agent.invoke(...)) using the OTel setup shown above.

For any of the frameworks above, adding trace propagation follows the same shape shown in the raw HTTP and OpenAI SDK tabs: start a span, derive traceparent, add it to defaultHeaders/headers, and run the framework call inside context.with(trace.setSpan(context.active(), rootSpan), ...).

Next steps