API และ SDKคู่มือ
สตรีมคำตอบแชทใน Node.js ด้วย OpenAI SDK
สตรีม chat completion ทีละโทเคนใน Node.js: ลูป for await หยุดตามต้องการ จัดการข้อผิดพลาด และ token usage ด้วย OpenAI SDK

ในหน้านี้
การสตรีมช่วยให้แอปของคุณแสดงคำตอบขณะที่กำลังถูกสร้าง แทนที่จะรอคำตอบทั้งหมด ในคู่มือนี้คุณจะสร้างสคริปต์ Node.js ขนาดเล็กที่สตรีม chat completion จาก CheapTokens API ทีละโทเคน หยุดได้ตามต้องการ และจัดการข้อผิดพลาด เวอร์ชัน Python ของหัวข้อนี้อยู่ใน Streaming responses in Python
สิ่งที่คุณต้องมี#
- Node.js 22 หรือใหม่กว่า (เวอร์ชันขั้นต่ำที่ระบุใน README ของแพ็กเกจ
openaiตรวจสอบเมื่อ 22 ตุลาคม 2026) - API key ของ CheapTokens หากยังไม่มี ให้ทำตาม Your first API request with CheapTokens
- SDK อย่างเป็นทางการ:
npm install openai
CheapTokens เปิดให้ใช้ API ที่เข้ากันได้กับ OpenAI ที่ https:/ ดังนั้นแพ็กเกจ openai อย่างเป็นทางการจึงใช้งานได้โดยเปลี่ยนเพียง base URL และ key เก็บ key ไว้ใน environment variable ไม่ใช่ในโค้ด:
export CHEAPTOKENS_API_KEY="your-key"ขั้นตอนที่ 1 สตรีมคำตอบด้วย for await#
สร้าง stream.mjs (นามสกุล .mjs ช่วยให้คุณใช้ import และ await ระดับบนสุดได้):
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CHEAPTOKENS_API_KEY,
baseURL: 'https://api.cheaptokens.dev/v1',
});
const stream = await client.chat.completions.create({
model: 'deepseek-ai/DeepSeek-V4-Flash-0731', // take the id from the CheapTokens docs
messages: [{ role: 'user', content: 'Explain streaming in two sentences.' }],
stream: true,
stream_options: { include_usage: true },
});
let usage = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
if (chunk.usage) usage = chunk.usage;
}
console.log('\n\nusage:', usage);รันด้วย node stream.mjs SDK จะอ่าน Server-Sent Events จาก API และส่ง chunk หนึ่งให้คุณต่อการวนซ้ำหนึ่งครั้งของ for await มีสามรายละเอียดที่สำคัญ:
- ข้อความมาใน
chunk.บาง chunk ไม่มีข้อความ (chunk แรกมักมีเพียง role) ดังนั้นตรวจสอบว่าchoices[0]. delta. content deltaไม่ว่าง process.พิมพ์โดยไม่ขึ้นบรรทัดใหม่ คำต่าง ๆ จึงปรากฏต่อเนื่องกันstdout. write - ด้วย
stream_chunk สุดท้ายจะมีออบเจกต์options: { include_ usage: true } usageพร้อมจำนวนโทเคน prompt และ completion และอาร์เรย์choicesของมันว่างเปล่า นั่นคือเหตุผลที่โค้ดใช้chunk.กับ optional chainingchoices[0]?.
model id มาจากเอกสาร CheapTokens: ผู้ให้บริการตั้งชื่อโมเดลเดียวกันต่างกัน ดังนั้นคัดลอก id จากที่นั่น
ขั้นตอนที่ 2 หยุดสตรีมตามต้องการ#
ผู้ใช้กด Stop ปิดแท็บ หรือสลับแชท ยกเลิกคำขอเพื่อไม่ให้คุณรอโทเคนที่ไม่มีใครจะอ่าน ส่ง AbortSignal ใน request options:
const controller = new AbortController();
process.on('SIGINT', () => controller.abort()); // Ctrl+C stops the stream
const stream = await client.chat.completions.create(
{
model: 'deepseek-ai/DeepSeek-V4-Flash-0731',
messages: [{ role: 'user', content: 'Write a long story about a lighthouse.' }],
stream: true,
},
{ signal: controller.signal },
);ออบเจกต์ stream ยังมี stream. ของตัวเอง และการออกจากลูป for await ด้วย break ก็ปิดการเชื่อมต่อเช่นกัน ใช้อันใดก็ได้ที่ใกล้กับโค้ดของคุณที่สุด: signal เมื่อปุ่มยกเลิกอยู่ที่อื่น, break เมื่อการตัดสินใจเกิดขึ้นภายในลูป (เช่น คุณเจอ stop word)
ขั้นตอนที่ 3 จัดการข้อผิดพลาด#
สตรีมอาจล้มเหลวก่อนโทเคนแรก (key ผิด ยอดเงินว่าง) หรือกลางทาง (เครือข่ายหลุด) ครอบทั้งคำขอและลูปไว้ใน try เดียว:
try {
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
} catch (err) {
if (err instanceof OpenAI.APIUserAbortError) {
console.log('\n[stopped by user]');
} else if (err instanceof OpenAI.APIError) {
console.error(`\nAPI error ${err.status}: ${err.message}`);
} else {
throw err;
}
}APIUserAbortErrorคือสิ่งที่คุณได้หลังจากabort()ของคุณเอง ไม่ใช่ความล้มเหลว ดังนั้นอย่าแสดงเป็นข้อผิดพลาดAPIErrorมี HTTPstatus: 401 หมายถึง key ผิดหรือไม่มี, 429 หมายถึงคำขอมากเกินไป และข้อความเกี่ยวกับโทเคนหมายถึงยอดเงินต้องเติมเพิ่ม สาเหตุและวิธีแก้เพิ่มเติมอยู่ใน Common connection errors and how to fix them และ "Not enough tokens" error- ข้อความที่พิมพ์ออกไปแล้วยังคงใช้ได้หลังเกิดข้อผิดพลาด เก็บไว้หากคุณต้องการแสดงคำตอบบางส่วน
ขั้นตอนที่ 4 อ่าน usage และประเมินค่าใช้จ่าย#
ออบเจกต์ usage เก็บ prompt_tokens และ completion_ เมื่อรวมกับราคาโมเดลต่อ 1M โทเคน จะให้ค่าใช้จ่ายที่แน่นอนของคำขอ สำหรับการประเมินคร่าว ๆ ก่อนรันอะไรก็ตาม ใช้ตารางสดด้านล่าง: แสดงค่าใช้จ่ายของ 1,000 คำขอ โดยแต่ละคำขอมี 2,000 โทเคน input และ 800 โทเคน output
| โมเดล | Input $/1M | Output $/1M | ต่อคำขอ | 1,000 คำขอ | OpenRouter, 1,000 คำขอ |
|---|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.000072 | $0.072 | $0.143 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.00011 | $0.11 | $0.22 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.00022 | $0.22 | $1.56 |
2,000 โทเค็นอินพุตและ 800 โทเค็นเอาต์พุตต่อคำขอ ราคาต่อ 1M โทเค็นเป็นราคาปัจจุบันและเปลี่ยนแปลงไปพร้อมกับอัตราค่าบริการของเรา คอลัมน์ OpenRouter แสดงราคาอ้างอิงสำหรับปริมาณงานเดียวกัน
สำหรับคำตอบแชทแบบสตรีม MiniMax-M2.7 ราคา $0.05 ต่อ 1M โทเคน input และ $0.15 ต่อ 1M โทเคน output พร้อม context 180K และ output สูงสุด 8,192 โทเคนต่อคำตอบ ราคาคงที่ต่อโมเดลในทุกช่วงเวลาของวัน และยอดเงินเดียวครอบคลุมทุกโมเดล ดังนั้นการเปลี่ยนสตริง model ก็เพียงพอที่จะเปรียบเทียบโมเดลด้วย prompt ของคุณเอง
สคริปต์เต็ม#
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CHEAPTOKENS_API_KEY,
baseURL: 'https://api.cheaptokens.dev/v1',
});
const controller = new AbortController();
process.on('SIGINT', () => controller.abort());
try {
const stream = await client.chat.completions.create(
{
model: 'deepseek-ai/DeepSeek-V4-Flash-0731',
messages: [{ role: 'user', content: 'Explain streaming in two sentences.' }],
stream: true,
stream_options: { include_usage: true },
},
{ signal: controller.signal },
);
let usage = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
if (chunk.usage) usage = chunk.usage;
}
console.log('\n\nusage:', usage);
} catch (err) {
if (err instanceof OpenAI.APIUserAbortError) {
console.log('\n[stopped by user]');
} else if (err instanceof OpenAI.APIError) {
console.error(`\nAPI error ${err.status}: ${err.message}`);
} else {
throw err;
}
}ขั้นตอนถัดไป#
- ส่ง chunk ไปยังเบราว์เซอร์ด้วย Server-Sent Events จาก endpoint ของคุณเอง เพื่อให้ key ไม่ออกจากเซิร์ฟเวอร์เลย
- ลองโมเดลอื่นโดยเปลี่ยนเพียงสตริง
model: MiniMax-M2.7 ใช้ได้บนยอดเงินเดียวกันและ base URL เดียวกัน - รับ key และรันสคริปต์: create a CheapTokens API key และเติมยอดเงิน
Endpoint, model id และขีดจำกัดของทุกโมเดลอยู่ใน CheapTokens docs



