n4nAI

TypeScript types for the OpenAI Node.js SDK

A practical guide to using openai node.js sdk typescript types: install, strict config, extend for custom params, streaming, tools, and compatible gateways.

n4n Team2 min read545 words

Audio narration

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

Getting the openai node.js sdk typescript types wired correctly from day one prevents a class of runtime errors that surface only in production. The official SDK ships comprehensive types, but they assume strict compiler settings and a specific usage pattern that many quickstarts skip. This guide walks through an ordered path to adopt those types in a real Node.js service.

Install and pin the SDK

Use the official package. Avoid @types/openai—it’s deprecated since the SDK ships its own types.

npm install openai@^4

Pin a minor version. The types evolve with each release; a major bump can rename namespaces or tighten unions in ways that break your build.

Enable strict TypeScript

The openai node.js sdk typescript types rely on strictNullChecks to flag missing await or undefined responses. Without it, you’ll silently lose safety on fields like message.content, which is string | null.

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "moduleResolution": "node",
    "target": "ES2022",
    "module": "ES2022",
    "useUnknownInCatchVariables": true
  }
}

If you run in an edge runtime, add "verbatimModuleSyntax": true so import type is enforced and the SDK isn’t bundled twice.

Use the exported types directly

Import types from the default export namespace. Don’t reconstruct request shapes by hand—the SDK’s discriminators catch role typos at compile time.

import OpenAI from 'openai';

type ChatReq = OpenAI.Chat.Completions.ChatCompletionCreateParams;
type ChatMsg = OpenAI.Chat.Completions.ChatCompletionMessageParam;

const messages: ChatMsg[] = [
  { role: 'system', content: 'You are terse.' },
  { role: 'user', content: 'Summarize.' }
];

Response typing

chat.completions.create returns ChatCompletion for non-stream, or Stream<ChatCompletionChunk> for stream. The return type is overloaded; let TypeScript infer it.

const resp = await client.chat.completions.create({ model: 'gpt-4o', messages });
// resp: ChatCompletion
const content: string | null = resp.choices[0].message.content;

Extend types for provider-specific parameters

OpenAI’s param types are closed. Standard fields like seed or logit_bias are present, but extra body fields trigger excess property errors.

Cast with an intersection when you must send custom data:

type ExtendedParams = ChatReq & { metadata?: Record<string, string> };

const req: ExtendedParams = {
  model: 'gpt-4o',
  messages,
  metadata: { tenant: 'acme' } // not in base type
};

await client.chat.completions.create(req as ChatReq);

Tradeoff: the cast bypasses compile-time checks on the extra field. If the field is critical, validate at runtime with zod rather than trusting the cast.

Streaming with async iterators

The openai node.js sdk typescript types differentiate ChatCompletion from ChatCompletionChunk. Use for await and let the stream: true literal drive the overload.

const stream = await client.chat.completions.create({
  model: 'gpt-4o',
  messages,
  stream: true
});

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content ?? '';
  process.stdout.write(delta);
}

Pitfall: deriving config from a variable drops the literal type. Use as const or inline the call.

const config = { model: 'gpt-4o', messages, stream: true } as const;
const stream = await client.chat.completions.create(config);

Tool and function calling types

Define tools with ChatCompletionTool. The SDK types the function parameters as a plain JSON schema object—no runtime validation is performed.

import type { ChatCompletionTool } from 'openai/resources/chat/completions';

const tools: ChatCompletionTool[] = [
  {
    type: 'function',
    function: {
      name: 'get_weather',
      parameters: {
        type: 'object',
        properties: { lat: { type: 'number' } },
        required: ['lat']
      }
    }
  }
];

When consuming tool calls, narrow on message.tool_calls, which is ChatCompletionMessageToolCall[] | undefined.

const msg = resp.choices[0].message;
if (msg.tool_calls) {
  for (const call of msg.tool_calls) {
    if (call.type === 'function') {
      const args = JSON.parse(call.function.arguments);
    }
  }
}

Error types and retries

The SDK throws OpenAI.APIError subclasses: RateLimitError, APIConnectionError, AuthenticationError. Under useUnknownInCatchVariables, err is unknown; instanceof narrows safely.

import OpenAI from 'openai';

try {
  await client.chat.completions.create({ model: 'gpt-4o', messages });
} catch (err) {
  if (err instanceof OpenAI.RateLimitError) {
    // exponential backoff
  } else if (err instanceof OpenAI.APIConnectionError) {
    // retry with jitter
  }
}

Don’t catch Error and assume it’s an API error—network failures and type guards demand precision.

Using with OpenAI-compatible gateways

If you point the SDK at an OpenAI-compatible endpoint like n4n.ai, the openai node.js sdk typescript types still describe the request and response shapes because the HTTP contract matches. Set baseURL and pass arbitrary model strings—the model field is typed as string, so provider-specific IDs need no cast.

const client = new OpenAI({
  baseURL: 'https://api.n4n.ai/v1',
  apiKey: process.env.N4N_KEY
});

await client.chat.completions.create({ model: 'anthropic/claude-3', messages });

The gateway may honor cache-control hints or routing directives; those are forwarded transparently and don’t alter the TypeScript surface.

Common pitfalls and tradeoffs

  • Excess property checks: Building request objects incrementally triggers errors. Construct the full object inline or assign to a typed variable before passing.
  • Null content: message.content is null for tool-call-only responses. Guard before string operations.
  • Strict mode overhead: You write more guards, but you eliminate an entire class of undefined crashes in async chains.
  • Type-only imports: Use import type for ChatCompletion* to keep edge bundles lean.
  • Version drift: Types are not semver-guaranteed across majors. Lock the version in CI and review the changelog before upgrading.

Minimal strict setup

// client.ts
import OpenAI from 'openai';

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

export type ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;
// usage.ts
import { openai, type ChatMessage } from './client';

const msgs: ChatMessage[] = [{ role: 'user', content: 'Hi' }];
const res = await openai.chat.completions.create({ model: 'gpt-4o', messages: msgs });
console.log(res.choices[0].message.content ?? '');

That’s the ordered path: install strict, use namespace types, extend carefully, handle streams and tools, catch typed errors, and widen only when routing through compatible gateways.

Tagsnodejstypescriptopenai-sdktypes

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 →