Migrating Node.js OpenAI SDK Applications: Base URL, Streaming, and Structured Outputs
A technical guide for Node.js developers migrating OpenAI SDK applications: configuring custom base URLs, managing authentication, handling streaming structured outputs, and diagnosing API errors.
On this page
Introduction
Migrating an existing Node.js application that uses the official openai SDK does not require rewriting your business logic. Because many unified model providers adhere strictly to the OpenAI REST specifications, adapting an existing application to run through an OpenAI-compatible routing layer—such as OneMux—requires only updating your client initialization parameters.
However, production pipelines often rely on advanced features: strict schema compliance for data extraction, token-by-token streaming, and robust error recovery. This guide covers how to reconfigure the Node.js OpenAI SDK, execute structured JSON schema extraction, handle streaming responses reliably, and diagnose execution errors.
Step 1: Base URL and Authentication Configuration
The official OpenAI Node.js SDK (openai v4+) accepts configuration overrides directly inside the OpenAI class constructor. To migrate your transport layer to an OpenAI-compatible endpoint, configure the baseURL property and set your API key.
SDK Client Initialization
import OpenAI from "openai";
// Initialize the client pointing to an OpenAI-compatible gateway
const client = new OpenAI({
apiKey: process.env.ONEMUX_API_KEY,
baseURL: "https://api.onemux.net/v1",
});
Migration Prerequisites
- Environment Variables: Ensure you separate provider keys in your
.envconfiguration (for example,ONEMUX_API_KEYvs.OPENAI_API_KEY). - Path Suffix: OpenAI's SDK automatically appends route paths like
/chat/completionsto thebaseURL. Always ensure yourbaseURLincludes the/v1suffix (e.g.,https://api.onemux.net/v1) unless your gateway handles root routing. - SDK Version: Verify that you are running
openaiversion^4.0.0or higher, as older v3 versions used a different configuration structure (Configurationclass).
To review supported routing rules and get started with API credentials, visit the OneMux Quickstart.
Step 2: Structured Extraction with Strict JSON Schema
Structured outputs force models to return output conforming exactly to a provided JSON Schema. According to OpenAI's documentation on structured outputs, setting "strict": true requires that:
- All object fields are listed in the
requiredarray. additionalProperties: falseis explicitly defined on all object schemas.- Optional fields are modeled as union types with
null(e.g.,["string", "null"]).
Standard Structured Completion Implementation
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.ONEMUX_API_KEY,
baseURL: "https://api.onemux.net/v1",
});
// Define strict JSON Schema for invoice extraction
const invoiceExtractionSchema = {
type: "object",
properties: {
vendorName: { type: "string" },
invoiceId: { type: "string" },
totalAmount: { type: "number" },
currency: { type: "string" },
items: {
type: "array",
items: {
type: "object",
properties: {
description: { type: "string" },
quantity: { type: "number" },
unitPrice: { type: "number" },
},
required: ["description", "quantity", "unitPrice"],
additionalProperties: false,
},
},
},
required: ["vendorName", "invoiceId", "totalAmount", "currency", "items"],
additionalProperties: false,
} as const;
async function extractInvoice(rawDocumentText: string) {
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "Extract normalized invoice records from the provided unstructured input.",
},
{
role: "user",
content: rawDocumentText,
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "invoice_extraction",
strict: true,
schema: invoiceExtractionSchema,
},
},
});
const choice = response.choices[0];
// Check for safety or policy refusals
if (choice.message.refusal) {
throw new Error(`Model refused extraction: ${choice.message.refusal}`);
}
const rawJson = choice.message.content;
if (!rawJson) {
throw new Error("Received empty response payload");
}
return JSON.parse(rawJson);
}
Compatibility with downstream gateways depends on whether the underlying provider endpoint supports json_schema mode with strict: true. For details on model capabilities across platforms, review the OneMux Models catalog.
Step 3: Handling Streaming with Structured Outputs
When stream: true is enabled alongside response_format, the model streams partial string fragments of the JSON payload. Unlike standard text chat, individual chunks cannot be parsed with JSON.parse() because they represent incomplete JSON tokens.
Accumulating Streamed JSON Chunks
To stream structured outputs properly, accumulate delta fragments until the stream completes, or pipe incoming chunks through an incremental JSON stream parser.
async function streamInvoiceExtraction(rawDocumentText: string) {
const stream = await client.chat.completions.create({
model: "gpt-4o",
stream: true,
messages: [
{
role: "system",
content: "Extract normalized invoice records from the provided text.",
},
{
role: "user",
content: rawDocumentText,
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "invoice_extraction",
strict: true,
schema: invoiceExtractionSchema,
},
},
});
let accumulatedJson = "";
let refusalReason: string | null = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.refusal) {
refusalReason = (refusalReason || "") + delta.refusal;
}
if (delta?.content) {
accumulatedJson += delta.content;
// Emit partial events or log progress if building a reactive UI
}
}
if (refusalReason) {
throw new Error(`Extraction was refused during stream: ${refusalReason}`);
}
try {
return JSON.parse(accumulatedJson);
} catch (err) {
throw new Error(`Failed to parse streamed JSON payload: ${accumulatedJson}`);
}
}
If your pipeline uses streaming strictly to minimize time-to-first-byte (TTFB) in user interfaces, verify that the downstream endpoint supports continuous Server-Sent Events (SSE) through your network proxy.
Step 4: Error Diagnosis and Troubleshooting
When migrating SDK calls to a custom baseURL, error diagnosis should distinguish between network proxy failures, schema misconfigurations, rate limits, and model refusals.
Common Errors and Resolution Strategies
| Error Code / Type | Root Cause | Diagnosis & Fix |
|---|---|---|
401 Unauthorized | Invalid or missing API key | Confirm that your apiKey contains a valid credential recognized by the target gateway (e.g., check ONEMUX_API_KEY). |
400 BadRequestError | Schema violation (strict: true) | Inspect error message for unsupported JSON schema keywords (e.g., missing additionalProperties: false or missing fields in required). |
404 NotFound | Incorrect path or model ID | Ensure baseURL ends with /v1. Confirm the model requested exists in the provider's active model list. |
429 RateLimitError | Concurrency or spend ceiling hit | Implement exponential backoff. Monitor quota limits via your provider dashboard. |
Refusal (Choice body) | Model safety guardrails triggered | Model generated an explicit refusal. Check choice.message.refusal rather than looking in content. |
Structured Error Handling Wrapper
import OpenAI from "openai";
async function safeExtractWrapper(fn: () => Promise<any>) {
try {
return await fn();
} catch (error: any) {
if (error instanceof OpenAI.APIError) {
console.error(`OpenAI API Error: status=${error.status}, type=${error.type}`);
console.error(`Message: ${error.message}`);
if (error.status === 400) {
console.error("Verify that your schema complies strictly with JSON Schema constraints.");
} else if (error.status === 401) {
console.error("Authentication failed: Check your bearer token and proxy baseURL.");
} else if (error.status === 429) {
console.warn("Rate limit reached. Queue request for delayed retry.");
}
} else {
console.error("Unexpected application error:", error);
}
throw error;
}
}
Step 5: Consolidating Providers via OneMux
Routing OpenAI SDK requests through OneMux simplifies multi-model operations without requiring third-party libraries or custom protocol adapters.
- Unified Endpoint: Maintain a single client interface while routing across models using standard OpenAI SDK semantics.
- Spend Management & Visibility: Centralize billing, review per-model usage, and set spending rules without managing multiple provider invoices.
- Flexible Pay-as-You-Go: Fund your account through flexible credit top-ups, preventing unexpected service interruptions.
To view current usage rates and compare routing options across models, consult the OneMux Pricing Guide and read the technical overview in the OneMux Documentation.
Sources
FAQ
Can I use Zod with the OpenAI SDK when using a custom base URL?
Yes. If your gateway fully implements the `/chat/completions` structured outputs spec, helper methods like `client.beta.chat.completions.parse()` and `zodResponseFormat()` work seamlessly by translating Zod schemas into the required strict JSON Schema payload.
Why does the model return a 400 error when using 'strict: true'?
The OpenAI structured outputs engine requires that every object definition explicitly includes `additionalProperties: false` and lists every declared property inside the `required` array. If any key is missing from `required`, or if unsupported JSON Schema keywords are present, the API rejects the request with a 400 validation error.
Does changing the baseURL affect SDK telemetry or timeouts?
Overriding `baseURL` redirects all HTTP transport to the new URL. Timeout and retry options (such as `maxRetries` or `timeout` in the OpenAI constructor) continue to function identically on the client side.
Related articles
Guides
Migrating a Node.js OpenAI SDK Application: Base URL, Streaming, and Human-in-the-Loop Triage
Learn how to migrate an existing Node.js OpenAI SDK application to an OpenAI-compatible gateway like OneMux, handle streaming, diagnose errors, and build a customer support triage workflow with human approval.
Guides
GPT 6.1 Terra Cost and Context Guide for Reliable JSON Extraction
Learn how to estimate GPT 6.1 Terra workload costs and bound context, retries, and output tokens for structured extraction with JSON Schema validation through OneMux.
Guides
GPT-6 Astra API Spend Management: Use a Premium Model Without Letting Agent Costs Drift
A practical framework for routing complex work to GPT-6 Astra, controlling token-driven spend, and operating one OpenAI-compatible API through OneMux.
Guides
OpenAI GPT 5.6 Terra API Pricing: How to Forecast and Control Cost Through OneMux
A developer-focused guide to GPT 5.6 Terra pricing on OneMux, including rate comparisons, cost formulas, routing patterns, spend controls, and implementation guidance.