The openai node.js sdk base url switch is the smallest change that lets you route existing code to a different inference backend without rewriting calls. Pointing that base URL at n4n.ai gives you one OpenAI-compatible endpoint that addresses 240+ models, so most deployments need only a configuration edit. This guide walks through the exact steps to repoint the SDK, handle auth, pass routing hints, and verify the traffic lands where you expect.
Step 1: Confirm SDK version and install if missing
The OpenAI Node.js SDK has shipped the baseURL option since v4.0. Earlier v3.x used basePath and a different internal fetch layer. If you are still on v3, upgrade or pin to a known-good v4 release. Check your tree:
npm list openai
Expected output for a modern install:
openai@4.56.0
If you need to install or upgrade:
npm install openai@latest
We recommend v4.56.0 or later; the baseURL surface is stable and the underlying fetch usage matches Node 18+ undici behavior. Keep your lockfile committed so the openai node.js sdk base url switch doesn’t silently drift between staging and production. If you are forced to stay on v3, replace baseURL with basePath in all following snippets and accept that timeout handling differs.
Step 2: Externalize credentials and the new base URL
Never hardcode keys or endpoints. Use process.env and a .env file loaded by your framework or dotenv. The OpenAI client reads OPENAI_API_KEY by default, but after a switch you should rename the variable to avoid confusion with the original vendor:
# .env
N4N_API_KEY="sk-your-real-key"
OPENAI_BASE_URL="https://api.n4n.ai/v1"
Note the /v1 suffix. The SDK appends /chat/completions (or /models) to whatever baseURL you supply. If you omit /v1, the final request hits https://api.n4n.ai/chat/completions and returns a 404 that masquerades as an auth failure. The openai node.js sdk base url switch is purely a client-side configuration; the remote server does not care what SDK you used to get there.
Load it explicitly if you are not using a framework that auto-loads:
import 'dotenv/config';
Step 3: Initialize the client with the overridden base URL
Create a small module that exports a configured client. Isolating the switch from business logic lets you run side-by-side clients during migration.
// lib/llm.ts
import OpenAI from 'openai';
const baseURL = process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1';
const apiKey = process.env.N4N_API_KEY ?? process.env.OPENAI_API_KEY;
if (!apiKey) {
throw new Error('No API key found for LLM client');
}
export const client = new OpenAI({
baseURL,
apiKey,
timeout: 30_000,
maxRetries: 2,
});
// Keep the original client around for gradual cutover
export const openaiDirect = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
If you call new OpenAI() without baseURL, it defaults to api.openai.com. The openai node.js sdk base url switch happens entirely in the baseURL field. Method names (chat.completions.create, embeddings.create) stay identical. TypeScript types are unchanged.
Step 4: Make a request against a non-OpenAI model
The point of an OpenAI-compatible gateway is model portability. After the switch, pass any model string the gateway supports. Example using a Claude model behind the endpoint:
import { client } from './lib/llm';
const res = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [
{ role: 'system', content: 'You are a terse senior engineer.' },
{ role: 'user', content: 'Explain base URL override in one line.' },
],
temperature: 0.2,
});
console.log(res.choices[0].message.content);
If the model name is unknown, the gateway returns a 400 with a list of available models. Treat model strings as configuration, not code. You can probe available models if the gateway implements the list endpoint:
const models = await client.models.list();
console.log(models.data.map((m) => m.id));
Not every gateway supports models.list; expect 501 if it doesn’t.
Step 5: Pass routing directives and cache hints
When you operate behind a gateway that aggregates providers, you can steer traffic per request. n4n.ai honors client routing directives and forwards provider cache-control hints, and provides automatic fallback when a provider is rate-limited. Use extra_headers to send gateway-specific keys without breaking the OpenAI shape:
const res = await client.chat.completions.create(
{
model: 'openai/gpt-4o-mini',
messages: [{ role: 'user', content: 'Ping' }],
},
{
extra_headers: {
'x-n4n-fallback': 'auto',
'x-n4n-cache': 'read-write',
},
},
);
This is not part of the standard OpenAI API; the SDK passes unknown headers through verbatim. If your gateway ignores them, they are harmless. The openai node.js sdk base url switch does not strip custom headers, so you keep full control over fallback and caching behavior. On the provider side, a read-write cache hint may map to Anthropic cache_control or OpenAI prompt caching depending on the resolved model.
Step 6: Verify the switch with a local mock
You need proof the traffic hits the new endpoint, not OpenAI. A local mock server catches path and header mistakes before they reach production.
// test/smoke.ts
import express from 'express';
import OpenAI from 'openai';
const app = express();
app.use(express.json());
app.post('/v1/chat/completions', (req, res) => {
res.json({
id: 'test',
object: 'chat.completion',
model: req.body.model,
choices: [{ message: { role: 'assistant', content: 'ok' }, finish_reason: 'stop' }],
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
});
});
app.listen(5050, async () => {
const baseURL = 'http://localhost:5050/v1';
const c = new OpenAI({ baseURL, apiKey: 'test' });
const r = await c.chat.completions.create({
model: 'test/model',
messages: [{ role: 'user', content: 'hi' }],
});
console.assert(r.choices[0].message.content === 'ok', 'should reach mock');
console.log('Verified base URL switch');
process.exit(0);
});
Run with ts-node test/smoke.ts. If the assertion passes, your client config is correct. Alternatively, check the usage object for per-token metering fields the gateway returns. OpenAI’s usage shape is mirrored, but gateway-specific trace IDs often appear under usage.metadata. Seeing those confirms the openai node.js sdk base url switch succeeded.
You can also curl the base URL directly to confirm it is alive:
curl -s $OPENAI_BASE_URL/models -H "Authorization: Bearer $N4N_API_KEY" | head
Step 7: Handle streaming and errors correctly
Streaming uses the same baseURL. No extra config:
const stream = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: 'Count to 3' }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}
Errors come back as OpenAI.APIError subclasses. Catch them and inspect error.status and error.headers:
try {
await client.chat.completions.create({ model: 'bad/model', messages: [] });
} catch (err) {
if (err instanceof OpenAI.APIError) {
console.error(err.status, err.headers['x-request-id']);
}
}
A 429 from the gateway means it is rate-limited upstream; automatic fallback (if enabled) should have already tried another provider. Consistent 401 means your apiKey is wrong or the base URL lacks /v1.
Step 8: Lock the configuration for production
Add a startup assertion so a missing suffix fails fast:
if (!process.env.OPENAI_BASE_URL?.endsWith('/v1')) {
throw new Error('OPENAI_BASE_URL must end with /v1');
}
Use a schema validator like Zod to load env:
import { z } from 'zod';
const Env = z.object({
OPENAI_BASE_URL: z.string().url().endsWith('/v1'),
N4N_API_KEY: z.string().min(20),
});
const env = Env.parse(process.env);
Deploy with the env var set in your orchestrator (Kubernetes Secrets, Vault, Doppler). The openai node.js sdk base url switch is now complete: your code calls client.chat.completions.create exactly as before, but the bytes go to the gateway.
Step 9: Rollback and gradual cutover
Keep the openaiDirect client from Step 3. Feature-flag the base URL:
const useGateway = process.env.USE_GATEWAY === '1';
const activeClient = useGateway ? client : openaiDirect;
This lets you revert in seconds if the gateway degrades. The openai node.js sdk base url switch is reversible because no call sites changed.
Common pitfalls
- Missing
/v1: The SDK appends/chat/completionstobaseURL. Without/v1you gethttps://host/chat/completions→ 404. - SDK v3: Uses
basePath, notbaseURL. Mixing them yields silent default routing. - Trailing slash:
https://host/v1/doubles the slash and may break some gateways. Use no trailing slash. - Axios interceptors: If you added request logging that mutates
config.url, ensure it respects absolute URLs set bybaseURL. - Proxy environments: Set
HTTPS_PROXYand passfetchoptions if your runtime needs a custom dispatcher. - Model string casing: Gateways are case-sensitive.
GPT-4owill 400;openai/gpt-4ois correct.
Why this matters for multi-model systems
Once the base URL points at a gateway, you can flip models per request without dependency changes. That decouples model availability from code shipping. The openai node.js sdk base url switch is the lever that turns a single-vendor integration into a portable one. Do the mock test in Step 6 before shipping; it catches 90% of misconfigurations and costs zero external tokens.