ChatGPT is a large language model (LLM) that understands and processes human prompts to produce helpful responses. OpenAI exposes models through the Chat Completions API. The official openai npm package is the practical way to call it from Node.js. This post covers common patterns beyond a single user message.
For the newer Responses API (web search, instructions, and input), see the dedicated post.
Prerequisites
- OpenAI account
- Generated API key
- Enabled billing
- Node.js version 26
openai package installed (npm i openai)- For Markdown output:
marked, dompurify, and jsdom (npm i marked dompurify jsdom)
Client setup
Create a client with your API key (read from the environment in production).
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
The same SDK can target OpenAI-compatible gateways by setting baseURL and apiKey:
const client = new OpenAI({
apiKey: process.env.LLM_API_KEY,
baseURL: 'https://your-gateway.example/v1',
});
Azure OpenAI uses AzureOpenAI instead. Many third-party hosts implement Chat Completions, so this API is a common integration path.
Basic integration
Send a user message and read the assistant reply from choices[0].message.content.
const completion = await client.chat.completions.create({
model: 'gpt-5.5',
messages: [
{ role: 'user', content: 'Write a one-sentence bedtime story about a unicorn.' },
],
});
console.log(completion.choices[0].message.content);
System prompt
Add a system message (or developer on newer models) before the user message to set tone, format, and role.
const completion = await client.chat.completions.create({
model: 'gpt-5.5',
messages: [
{ role: 'system', content: 'Reply in one short sentence. Use plain language.' },
{ role: 'user', content: 'Explain what an LLM is.' },
],
});
console.log(completion.choices[0].message.content);
Few-shot prompting
Include prior user and assistant turns in messages, then the new user question. Keep task rules in the system message.
const completion = await client.chat.completions.create({
model: 'gpt-5.5',
messages: [
{
role: 'system',
content:
'Classify sentiment as exactly one word: positive, negative, or neutral.',
},
{ role: 'user', content: 'I love this!' },
{ role: 'assistant', content: 'positive' },
{ role: 'user', content: 'This is awful.' },
{ role: 'assistant', content: 'negative' },
{ role: 'user', content: 'It is fine I guess.' },
],
});
console.log(completion.choices[0].message.content);
Streaming
Set stream: true and read incremental text from choices[0].delta.content.
const stream = await client.chat.completions.create({
model: 'gpt-5.5',
messages: [{ role: 'user', content: 'List three colors.' }],
stream: true,
});
process.stdout.write('[stream] ');
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) {
process.stdout.write(delta);
}
}
process.stdout.write('\n');
Structured output with JSON schema
Use response_format with type: 'json_schema' and strict: true so the model returns JSON matching your schema.
const completion = await client.chat.completions.create({
model: 'gpt-5.5',
messages: [
{
role: 'user',
content: 'The film Inception was directed by Christopher Nolan.',
},
],
response_format: {
type: 'json_schema',
json_schema: {
name: 'movie_summary',
strict: true,
schema: {
type: 'object',
properties: {
title: { type: 'string' },
director: { type: 'string' },
},
required: ['title', 'director'],
additionalProperties: false,
},
},
},
});
const data = JSON.parse(completion.choices[0].message.content);
console.log(data.title, data.director);
For typed parsing with Zod, you can use client.chat.completions.parse() instead of JSON.parse.
Markdown output to HTML
Ask for Markdown in the system message, then convert the assistant reply to HTML and sanitize before rendering.
import { marked } from 'marked';
import { JSDOM } from 'jsdom';
import DOMPurify from 'dompurify';
const purify = DOMPurify(new JSDOM('').window);
const completion = await client.chat.completions.create({
model: 'gpt-5.5',
messages: [
{
role: 'system',
content: 'Reply in Markdown only. Use a heading and a short bullet list.',
},
{ role: 'user', content: 'Explain what an LLM is in three bullet points.' },
],
});
const markdown = completion.choices[0].message.content;
const html = marked.parse(markdown);
const safeHtml = purify.sanitize(html);
Always run DOMPurify.sanitize on model-generated HTML. The model can emit unsafe markup; sanitization strips scripts and other dangerous content.
Demo
Runnable scripts for each section live in the openai-chat-completions-api-demo folder. Get access via code demos.