n4nAI

How to switch the OpenAI Node.js SDK base URL to n4n.ai

Learn how to repoint the OpenAI Node.js SDK to a different OpenAI-compatible endpoint with a base URL change, including code and verification steps.

n4n Team4 min read976 words

Audio narration

Coming soon — every post will get a voice note here.

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/completions to baseURL. Without /v1 you get https://host/chat/completions → 404.
  • SDK v3: Uses basePath, not baseURL. 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 by baseURL.
  • Proxy environments: Set HTTPS_PROXY and pass fetch options if your runtime needs a custom dispatcher.
  • Model string casing: Gateways are case-sensitive. GPT-4o will 400; openai/gpt-4o is 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.

Tagsnodejsopenai-sdkbase-urlmulti-model

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All node.js openai-compatible sdk integration posts →