# Migrate an OpenAI Node.js SDK app: a compatibility checklist

Changing the base URL is only one part of a migration. Your SDK method, model, input fields and response parser must agree with the target route. This guide provides a migration worksheet and a small Node.js example; it is not a claim that every OpenAI API feature is available on every model.

## Record the application contract

Before editing your application, fill out this worksheet from the code that actually sends requests:

| Item | Record before migration | Check on the target route |
| --- | --- | --- |
| SDK and version | The exact `openai` version in the lockfile | Pin and test that version |
| Method | `chat.completions.create` or `responses.create` | The corresponding endpoint is supported |
| Input | Messages or input items, system instructions, attachments | Role layout, media and optional fields are accepted |
| Output | Text, tool calls, structured data, finish state | Your parser handles the native response |
| Recovery | SDK retries, application retries, timeout | The combined attempt budget is intentional |

Use [model discovery](/docs/common-api) to obtain the public model ID, then read its model guide. `/v1/models` identifies protocol and declared capabilities; it does not list every endpoint. An `openai` protocol label alone is not proof of Responses support. See [protocol selection](/docs/llm/protocols) for the endpoint families.

## Send one minimal Chat Completions request

Install `openai` in your server project and commit the resulting lockfile. Save this as `migration-check.mjs`. Set `API_KEY` and `MODEL_ID` in the server environment; select a model whose guide explicitly supports Chat Completions. Running the script makes one billable generation request.

```javascript
import OpenAI from 'openai';

const { API_KEY, MODEL_ID } = process.env;
if (!API_KEY || !MODEL_ID) throw new Error('Set API_KEY and MODEL_ID');
const client = new OpenAI({
  apiKey: API_KEY,
  baseURL: 'https://vip.astralgames.ai/v1',
  maxRetries: 0,
  timeout: 60_000,
});
const response = await client.chat.completions.create({
  model: MODEL_ID,
  messages: [{ role: 'user', content: 'Explain HTTP in three short steps.' }],
  stream: false,
});
const choice = response.choices?.[0];
if (!choice) throw new Error('No completion choice returned');
console.log({
  text: choice.message.content,
  finishReason: choice.finish_reason,
  usage: response.usage,
});
```

The client base URL ends in `/v1`; this SDK method appends `/chat/completions`. Do not use the complete endpoint as `baseURL`, or append a second `/v1`. Keep the platform key on your server and send it only to the platform origin. Do not enable browser-side SDK use to work around a key configuration error.

The [official Node SDK documentation](https://github.com/openai/openai-node#retries) describes automatic retries and timeout configuration (checked 2026-11-03). `maxRetries: 0` deliberately disables SDK retries for this validation. A local timeout does not establish that the remote request stopped or that no usage occurred; inspect recorded usage before another attempt.

## Add features separately

Once the plain-text request works, restore one feature at a time: an output limit, streaming, tool calls, structured output, then media inputs if needed. Use the selected model's accepted field names; do not assume sampling and token-limit parameters are interchangeable.

For Responses, change the method, request body and parser together. A Responses output item is not a Chat Completions `choices[0]` object. Likewise, `stream: true` returns an event stream, not the complete JSON object used above. Follow the [streaming guide](/docs/llm/streaming) before adapting the parser.

Tool calls still require your application to validate arguments, authorize the action and return a tool result. A structured response still needs application-level validation. Model output does not prove that a tool ran or that a business rule passed.

## Validate failures before moving production traffic

Use local response fixtures to cover missing choices, null text with tool calls, truncation, refusal, malformed output and interrupted streams. Mock network failures to verify that the SDK and application together do not silently duplicate requests. Do not create paid requests merely to exercise error branches.

For an approved live sample, keep model ID, endpoint, SDK version, sanitized request parameters, finish state and usage together. Compare actual task acceptance with the [evaluation worksheet](/docs/llm/evaluation), and reconcile charges with [cost and usage](/docs/llm/cost-and-usage). Preserve the previous adapter so you can revert your application configuration if compatibility checks fail.
