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.
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)
- Gateway Integration
- With trace (optional)
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"}]}'
const rootSpan = tracer.startSpan("gateway_call");
const { traceId, spanId } = rootSpan.spanContext();
const traceparent = `00-${traceId}-${spanId}-01`;
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,
traceparent,
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }],
}),
});
const body = await res.json();
rootSpan.end();
curl -X POST "${GATEWAY_URL}/openai/chat/completions" \
-H "Authorization: Bearer ${GATEWAY_KEY}" \
-H "X-Provider-Key: ${OPENAI_API_KEY}" \
-H "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'
Native SDKs
- OpenAI
- Anthropic
- Gemini
- Groq / OpenRouter / Ollama
- Azure OpenAI
- Gateway Integration
- With trace (optional)
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" }],
});
import OpenAI from "openai";
const rootSpan = tracer.startSpan("openai_call");
const { traceId, spanId } = rootSpan.spanContext();
const traceparent = `00-${traceId}-${spanId}-01`;
const client = new OpenAI({
apiKey: process.env.GATEWAY_KEY,
baseURL: `${process.env.GATEWAY_URL}/openai`,
defaultHeaders: { "X-Provider-Key": process.env.OPENAI_API_KEY, traceparent },
});
const completion = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }],
});
rootSpan.end();
- Gateway Integration
- With trace (optional)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: "unused",
authToken: process.env.GATEWAY_KEY,
baseURL: `${process.env.GATEWAY_URL}/anthropic`,
defaultHeaders: { "X-Provider-Key": process.env.ANTHROPIC_API_KEY },
});
const message = await client.messages.create({
model: "claude-3-5-haiku-20241022",
max_tokens: 256,
messages: [{ role: "user", content: "hello" }],
});
import Anthropic from "@anthropic-ai/sdk";
const rootSpan = tracer.startSpan("anthropic_call");
const { traceId, spanId } = rootSpan.spanContext();
const traceparent = `00-${traceId}-${spanId}-01`;
const client = new Anthropic({
apiKey: "unused",
authToken: process.env.GATEWAY_KEY,
baseURL: `${process.env.GATEWAY_URL}/anthropic`,
defaultHeaders: { "X-Provider-Key": process.env.ANTHROPIC_API_KEY, traceparent },
});
const message = await client.messages.create({
model: "claude-3-5-haiku-20241022",
max_tokens: 256,
messages: [{ role: "user", content: "hello" }],
});
rootSpan.end();
- Gateway Integration
- With trace (optional)
import { GoogleGenAI } from "@google/genai";
const genAI = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
httpOptions: {
baseUrl: `${process.env.GATEWAY_URL}/gemini`,
headers: { "X-Gateway-Key": process.env.GATEWAY_KEY },
},
});
const response = await genAI.models.generateContent({
model: "gemini-flash-lite-latest",
contents: "hello",
});
import { GoogleGenAI } from "@google/genai";
const rootSpan = tracer.startSpan("gemini_call");
const { traceId, spanId } = rootSpan.spanContext();
const traceparent = `00-${traceId}-${spanId}-01`;
const genAI = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
httpOptions: {
baseUrl: `${process.env.GATEWAY_URL}/gemini`,
headers: { "X-Gateway-Key": process.env.GATEWAY_KEY, traceparent },
},
});
const response = await genAI.models.generateContent({
model: "gemini-flash-lite-latest",
contents: "hello",
});
rootSpan.end();
These are OpenAI-compatible through the gateway, so reuse the openai package with a different baseURL and provider key.
Without trace:
new OpenAI({ apiKey: GATEWAY_KEY, baseURL: `${GATEWAY_URL}/groq`, defaultHeaders: { "X-Provider-Key": GROQ_API_KEY } })
new OpenAI({ apiKey: GATEWAY_KEY, baseURL: `${GATEWAY_URL}/openrouter`, defaultHeaders: { "X-Provider-Key": OPENROUTER_API_KEY } })
new OpenAI({ apiKey: GATEWAY_KEY, baseURL: `${GATEWAY_URL}/ollama/v1`, defaultHeaders: { "X-Provider-Key": OLLAMA_API_KEY } })
With trace:
new OpenAI({ apiKey: GATEWAY_KEY, baseURL: `${GATEWAY_URL}/groq`, defaultHeaders: { "X-Provider-Key": GROQ_API_KEY, traceparent } })
new OpenAI({ apiKey: GATEWAY_KEY, baseURL: `${GATEWAY_URL}/openrouter`, defaultHeaders: { "X-Provider-Key": OPENROUTER_API_KEY, traceparent } })
new OpenAI({ apiKey: GATEWAY_KEY, baseURL: `${GATEWAY_URL}/ollama/v1`, defaultHeaders: { "X-Provider-Key": OLLAMA_API_KEY, traceparent } })
Without trace:
new OpenAI({
apiKey: GATEWAY_KEY,
baseURL: `${GATEWAY_URL}/azure`,
defaultHeaders: { "api-key": AZURE_OPENAI_KEY, "X-Azure-Endpoint": AZURE_OPENAI_ENDPOINT },
})
With trace:
new OpenAI({
apiKey: GATEWAY_KEY,
baseURL: `${GATEWAY_URL}/azure`,
defaultHeaders: { "api-key": AZURE_OPENAI_KEY, "X-Azure-Endpoint": AZURE_OPENAI_ENDPOINT, traceparent },
})
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.
- LangChain / LangGraph
- Vercel AI SDK
- Mastra
- OpenAI Agents SDK
- Semantic Kernel
- LlamaIndex.TS
- Google ADK (JS)
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.
import { generateText } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
const openai = createOpenAI({
baseURL: `${process.env.GATEWAY_URL}/openai`,
apiKey: process.env.GATEWAY_KEY,
headers: { "X-Provider-Key": process.env.OPENAI_API_KEY },
});
const { text } = await generateText({ model: openai("gpt-4o-mini"), prompt: "hello" });
import { Agent } from "@mastra/core/agent";
import { createOpenAI } from "@ai-sdk/openai";
const openai = createOpenAI({
baseURL: `${process.env.GATEWAY_URL}/openai`,
apiKey: process.env.GATEWAY_KEY,
headers: { "X-Provider-Key": process.env.OPENAI_API_KEY },
});
const agent = new Agent({ name: "agent", model: openai("gpt-4o-mini"), tools: {} });
const result = await agent.generate("hello");
import OpenAI from "openai";
import { Agent, run, OpenAIChatCompletionsModel } from "@openai/agents";
const openaiClient = new OpenAI({
apiKey: process.env.GATEWAY_KEY,
baseURL: `${process.env.GATEWAY_URL}/openai`,
defaultHeaders: { "X-Provider-Key": process.env.OPENAI_API_KEY },
});
const agent = new Agent({
name: "agent",
model: new OpenAIChatCompletionsModel(openaiClient, "gpt-4o-mini"),
});
const result = await run(agent, "hello");
import OpenAI from "openai";
import { OpenAIChatClient } from "@semantic-kernel/openai";
import { functionInvocation, ChatMessage } from "@semantic-kernel/ai";
const openAIClient = new OpenAI({
baseURL: `${process.env.GATEWAY_URL}/openai`,
apiKey: process.env.GATEWAY_KEY,
defaultHeaders: { "X-Provider-Key": process.env.OPENAI_API_KEY },
});
const chatClient = functionInvocation(new OpenAIChatClient({ openAIClient, modelId: "gpt-4o-mini" }));
const response = await chatClient.getResponse([new ChatMessage({ role: "user", content: "hello" })]);
import { agent } from "llamaindex";
import { OpenAI } from "@llamaindex/openai";
const llm = new OpenAI({
model: "gpt-4o-mini",
apiKey: process.env.GATEWAY_KEY,
additionalSessionOptions: {
baseURL: `${process.env.GATEWAY_URL}/openai`,
defaultHeaders: { "X-Provider-Key": process.env.OPENAI_API_KEY },
},
});
const myAgent = agent({ llm, tools: [] });
const result = await myAgent.run("hello");
import { GoogleGenAI } from "@google/genai";
import { Gemini, LlmAgent, InMemorySessionService, Runner } from "@google/adk";
const gemini = new Gemini({ model: "gemini-flash-lite-latest" });
gemini._apiClient = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
httpOptions: {
baseUrl: `${process.env.GATEWAY_URL}/gemini`,
headers: { "X-Gateway-Key": process.env.GATEWAY_KEY },
},
});
const agent = new LlmAgent({ name: "agent", model: gemini, tools: [] });
const runner = new Runner({ agent, sessionService: new InMemorySessionService() });
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
- OpenAI SDK Integration - the reference walkthrough this gateway is built around, including streaming.
- Other Providers - full provider/URL-segment reference.
- Gateway Authentication - key precedence and header details.
- Trace Context - how
traceparentcorrelates observations.