n4nAI

OpenAI Node.js SDK authentication with environment variables

Learn how to configure OpenAI Node.js SDK authentication using environment variables securely, with setup steps, code samples, and pitfall callouts.

n4n Team5 min read1,037 words

Audio narration

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

Most teams wire up the OpenAI Node.js SDK in a hurry and paste API keys directly into source files. Proper openai node.js sdk authentication env vars configuration keeps secrets out of version control, simplifies rotation, and lets you run the same code against staging and production. This guide lays out a hardened, ordered path from project init to key rotation, with the pitfalls I see in production reviews.

Why env vars beat hardcoded keys

Hardcoding sk-... strings in a repo is how credentials leak. Even private repos get cloned to laptops, bundled into images, and scanned by bots. Using openai node.js sdk authentication env vars moves the secret to the runtime environment, where access controls and secret managers can govern it.

Environment variables also let you swap credentials per deployment without rebuilding artifacts. A CI job can inject a test key; production uses a vault-backed value. The SDK itself does not care where the string came from.

Project setup and dependencies

Start with a clean Node project. Use Node 18+ for native fetch and ESM support.

Installing the SDK

npm init -y
npm install openai
npm install dotenv

The openai package is the official SDK. dotenv loads a local .env file into process.env during development.

Creating a .env file

Never put real secrets in a committed file. Create .env locally and add it to .gitignore.

echo "OPENAI_API_KEY=sk-test-12345" > .env
echo ".env" >> .gitignore

For production, inject the variable through your platform (Vercel, Fly, Kubernetes Secrets) rather than shipping the file.

Loading variables the right way

In development, dotenv is convenient. In production, the OS or orchestrator already provides process.env. Calling dotenv.config() there is harmless but redundant.

dotenv vs process.env in production

// at the top of your entry file
if (process.env.NODE_ENV !== 'production') {
  require('dotenv').config();
}

const apiKey = process.env.OPENAI_API_KEY;

This pattern avoids masking real env vars with a stale local file. If the platform sets the variable, dotenv will not overwrite it by default, but the guard makes intent explicit.

Typing your config

If you use TypeScript, declare the expected shape:

declare global {
  namespace NodeJS {
    interface ProcessEnv {
      OPENAI_API_KEY: string;
      OPENAI_BASE_URL?: string;
    }
  }
}

This catches missing vars at compile time in strict mode, though runtime checks are still required.

Initializing the client with env vars

The SDK reads apiKey from the constructor. Do not read it from a JSON config or a remote fetch at call time; pass it once at boot.

Basic OpenAI client

import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

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

If apiKey is undefined, the SDK throws at request time with a vague error. We will fix that with fail-fast validation below.

Pointing at an OpenAI-compatible gateway

Many teams proxy through a gateway to access multiple model providers. If you route through a gateway such as n4n.ai, set OPENAI_BASE_URL to its endpoint and reuse the same key variable; the gateway honors the standard auth header and adds fallback across providers.

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  baseURL: process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
});

The baseURL override is the only change needed for any OpenAI-compatible endpoint. Keep the variable name OPENAI_API_KEY to avoid confusing tooling.

Handling missing or malformed vars

A silent undefined key produces late, confusing failures. Validate at process start.

Fail fast at boot

function loadConfig() {
  const apiKey = process.env.OPENAI_API_KEY;
  if (!apiKey || !apiKey.startsWith('sk-')) {
    throw new Error('OPENAI_API_KEY is missing or malformed');
  }
  return { apiKey, baseURL: process.env.OPENAI_BASE_URL };
}

const config = loadConfig();
const openai = new OpenAI(config);

This crashes the service before it accepts traffic, which is preferable to 500s on every request.

Validation with zod

For larger apps, use a schema:

import { z } from 'zod';

const EnvSchema = z.object({
  OPENAI_API_KEY: z.string().startsWith('sk-'),
  OPENAI_BASE_URL: z.string().url().optional(),
});

const parsed = EnvSchema.parse(process.env);

Zod gives clear error messages and centralizes env contracts.

Rotating keys without downtime

OpenAI supports multiple active keys. If you use a secret manager, rotate by issuing a new key, updating the env var, and restarting or sending a SIGHUP to reload.

For zero-downtime in long-running processes, build a client factory that reads the key from a getter:

let currentKey = process.env.OPENAI_API_KEY!;
function rotateKey(newKey: string) { currentKey = newKey; }

const openai = new OpenAI({
  apiKey: () => currentKey,
});

The SDK accepts a function for apiKey, evaluating it per request. This avoids process restarts during rotation.

Tradeoff: a function getter slightly increases per-call overhead and can complicate testing. For most services, env reload + restart is simpler.

Using multiple keys for rate limits

If you hit provider rate limits, round-robin across several keys from a comma-separated env var:

const keys = (process.env.OPENAI_API_KEYS ?? '').split(',').filter(Boolean);
let i = 0;
const openai = new OpenAI({
  apiKey: () => keys[i++ % keys.length],
});

This is a client-side workaround. A gateway such as n4n.ai performs automatic fallback when a provider is rate-limited or degraded, removing the operational burden of key pools.

Testing with mocked environment

The same openai node.js sdk authentication env vars approach works in tests with fake values. Never import the real .env in test runs.

Isolating test config

// test/setup.ts
process.env.OPENAI_API_KEY = 'sk-test-fake';
process.env.OPENAI_BASE_URL = 'http://localhost:4010/v1'; // mock server

Use a mock like msw or openai-mock to stub responses. This keeps tests hermetic and avoids accidental billing.

CI secrets

In GitHub Actions, set the var under env: from a repo secret. Never echo it.

jobs:
  test:
    steps:
      - run: npm test
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

Tradeoff: storing a real key in CI lets integration tests run, but increases blast radius if the runner is compromised. Prefer recorded fixtures.

Common pitfalls

Accidental logging of keys

Never log process.env wholesale. Structured loggers may serialize the whole object. Redact:

console.log('config', { baseURL: config.baseURL, keySet: !!config.apiKey });

Committing .env to git

Use git secrets or a pre-commit hook. Once a key hits history, rotate it—removing the file later does not purge the leak.

Mixing server and client env

The OpenAI SDK runs on the server. Exposing OPENAI_API_KEY to browser bundles via NEXT_PUBLIC_ or similar prefixes is a critical mistake. Client-side calls must go through your own backend proxy.

Base URL overrides and proxy pitfalls

If you set baseURL without a trailing /v1, the SDK appends paths incorrectly. Always include the full base including /v1 for OpenAI, or follow the gateway’s docs. Misconfigured proxies also strip the Authorization header; test with a curl.

curl -H "Authorization: Bearer $OPENAI_API_KEY" $OPENAI_BASE_URL/models

Case sensitivity and shell quoting

Env var names are case-sensitive on Linux. openai_api_key will not be read. Quote values containing special characters in shell exports to avoid truncation.

Tradeoffs of env var auth

Env vars are simple but not encrypted at rest. On a shared host, any process can read them. For high-security workloads, use a secret manager that mounts files or provides short-lived tokens. Env vars also cannot enforce per-tenant isolation; if you need that, proxy the SDK calls through a service that swaps keys contextually.

Another tradeoff: env vars are process-global. If you serve multiple customers with different keys, you must instantiate multiple clients or use the function getter pattern. That is fine at small scale but adds memory overhead.

Quick reference checklist

  • Install openai and dotenv; gitignore .env.
  • Load env early; guard dotenv in non-production.
  • Validate OPENAI_API_KEY at boot with a prefix check or zod.
  • Pass apiKey and optional baseURL from env to the client constructor.
  • Use a function getter only if you need hot rotation.
  • Redact keys from logs; never expose to client code.
  • Rotate by updating the env and restarting, or via getter for zero-downtime.

Following this path makes openai node.js sdk authentication env vars a non-event in your deploy pipeline rather than a recurring incident.

Tagsnodejsopenai-sdkauthenticationenvironment-variables

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 →