API & SDKPanduan
Streaming respons chat di Node.js dengan OpenAI SDK
Streaming chat completion token demi token di Node.js: loop for await, berhenti sesuai permintaan, penanganan error, dan penggunaan token dengan OpenAI SDK.

Di halaman ini
Streaming memungkinkan aplikasi Anda menampilkan jawaban saat sedang dihasilkan alih-alih menunggu balasan lengkap. Dalam panduan ini Anda akan membangun skrip Node.js kecil yang melakukan streaming chat completion dari API CheapTokens token demi token, menghentikannya sesuai permintaan, dan menangani error. Versi Python dari topik ini ada di Streaming responses in Python.
Yang Anda butuhkan#
- Node.js 22 atau lebih baru (versi minimum yang tercantum di README paket
openai, diperiksa 22 Oktober 2026). - Kunci API CheapTokens. Jika Anda belum memilikinya, ikuti Your first API request with CheapTokens.
- SDK resmi:
npm install openai.
CheapTokens menyediakan API yang kompatibel dengan OpenAI di https:/, jadi paket resmi openai berfungsi hanya dengan mengubah base URL dan kuncinya. Simpan kunci di variabel environment, bukan di dalam kode:
export CHEAPTOKENS_API_KEY="your-key"Langkah 1. Streaming jawaban dengan for await#
Buat stream.mjs (ekstensi .mjs memungkinkan Anda menggunakan import dan await tingkat atas):
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);Jalankan dengan node stream.mjs. SDK membaca Server-Sent Events dari API dan memberi Anda satu chunk per iterasi for await. Tiga detail penting:
- Teks tiba di
chunk.. Beberapa chunk tidak membawa teks (chunk pertama sering hanya berisi role), jadi periksa bahwachoices[0]. delta. content deltatidak kosong. process.mencetak tanpa jeda baris, sehingga kata-kata muncul satu demi satu.stdout. write - Dengan
stream_chunk terakhir membawa objekoptions: { include_ usage: true } usageberisi jumlah token prompt dan completion, dan arraychoices-nya kosong. Itulah sebabnya kode menggunakanchunk.dengan optional chaining.choices[0]?.
Id model berasal dari dokumentasi CheapTokens: penyedia menamai model yang sama secara berbeda, jadi salin id dari sana.
Langkah 2. Hentikan stream sesuai permintaan#
Pengguna menekan Stop, menutup tab, atau berpindah chat. Batalkan permintaan agar Anda tidak menunggu token yang tidak akan dibaca siapa pun. Berikan AbortSignal di opsi permintaan:
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 },
);Objek stream juga memiliki stream. sendiri, dan keluar dari loop for await dengan break juga menutup koneksi. Gunakan mana yang lebih dekat dengan kode Anda: signal ketika tombol batal berada di tempat lain, break ketika keputusan dibuat di dalam loop (misalnya, Anda menemukan stop word).
Langkah 3. Tangani error#
Stream dapat gagal sebelum token pertama (kunci salah, saldo kosong) atau di tengah (koneksi terputus). Bungkus permintaan dan loop dalam satu 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;
}
}APIUserAbortErroradalah yang Anda dapatkan setelahabort()Anda sendiri. Ini bukan kegagalan, jadi jangan tampilkan sebagai kegagalan.APIErrormemilikistatusHTTP: 401 berarti kunci salah atau tidak ada, 429 berarti terlalu banyak permintaan, dan pesan tentang token berarti saldo perlu diisi ulang. Penyebab dan perbaikan lainnya ada di Common connection errors and how to fix them dan "Not enough tokens" error.- Teks yang sudah dicetak tetap valid setelah error. Simpan jika Anda ingin menampilkan jawaban parsial.
Langkah 4. Baca usage dan perkirakan biaya#
Objek usage berisi prompt_tokens dan completion_. Bersama dengan harga model per 1M token, ini memberi Anda biaya persis dari sebuah permintaan. Untuk perkiraan kasar sebelum menjalankan apa pun, gunakan tabel langsung di bawah: tabel ini menunjukkan biaya 1.000 permintaan dengan 2.000 token input dan 800 token output masing-masing.
| Model | Input $/1Jt | Output $/1Jt | Per permintaan | 1.000 permintaan | OpenRouter, 1.000 permintaan |
|---|---|---|---|---|---|
| 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 token input dan 800 token output per permintaan. Harga per 1 juta token adalah harga saat ini dan berubah seiring tarif kami; kolom OpenRouter menunjukkan harga referensi untuk beban kerja yang sama.
Untuk balasan chat yang di-stream, MiniMax-M2.7 dihargai $0.05 per 1M token input dan $0.15 per 1M token output, dengan konteks 180K dan hingga 8.192 token output per jawaban. Harga tetap per model pada waktu kapan pun, dan satu saldo mencakup semua model, jadi mengganti string model sudah cukup untuk membandingkannya pada prompt Anda sendiri.
Skrip lengkap#
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;
}
}Langkah selanjutnya#
- Kirim chunk ke browser dengan Server-Sent Events dari endpoint Anda sendiri, sehingga kunci tidak pernah meninggalkan server.
- Coba model lain dengan hanya mengubah string
model: MiniMax-M2.7 tersedia pada saldo yang sama dan base URL yang sama. - Dapatkan kunci dan jalankan skrip: create a CheapTokens API key dan isi ulang saldo.
Endpoint, id model, dan batas untuk setiap model ada di CheapTokens docs.



