API e SDKGuida
Esegui lo streaming delle risposte chat in Node.js con l'SDK OpenAI
Esegui lo streaming di un completamento chat token per token in Node.js: ciclo for await, interruzione su richiesta, gestione degli errori e utilizzo dei token con l'SDK OpenAI.

In questa pagina
Lo streaming consente alla tua app di mostrare la risposta mentre viene generata invece di attendere la risposta completa. In questa guida creerai un piccolo script Node.js che esegue lo streaming di un completamento chat dall'API CheapTokens token per token, lo interrompe su richiesta e gestisce gli errori. La versione Python di questo argomento è in Streaming responses in Python.
Cosa ti serve#
- Node.js 22 o successivo (la versione minima indicata nel README del pacchetto
openai, verificata il 22 ottobre 2026). - Una chiave API CheapTokens. Se non ne hai ancora una, segui Your first API request with CheapTokens.
- L'SDK ufficiale:
npm install openai.
CheapTokens espone un'API compatibile con OpenAI su https:/, quindi il pacchetto ufficiale openai funziona cambiando solo l'URL di base e la chiave. Mantieni la chiave in una variabile d'ambiente, non nel codice:
export CHEAPTOKENS_API_KEY="your-key"Passo 1. Esegui lo streaming della risposta con for await#
Crea stream.mjs (l'estensione .mjs ti permette di usare import e await di primo livello):
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);Eseguilo con node stream.mjs. L'SDK legge i Server-Sent Events dall'API e ti fornisce un chunk per ogni iterazione di for await. Tre dettagli sono importanti:
- Il testo arriva in
chunk.. Alcuni chunk non contengono testo (il primo spesso contiene solo il ruolo), quindi verifica chechoices[0]. delta. content deltanon sia vuoto. process.stampa senza interruzione di riga, quindi le parole appaiono una dopo l'altra.stdout. write - Con
stream_l'ultimo chunk contiene un oggettooptions: { include_ usage: true } usagecon i conteggi dei token di prompt e di completamento, e il suo arraychoicesè vuoto. Ecco perché il codice usachunk.con l'optional chaining.choices[0]?.
L'id del modello proviene dalla documentazione di CheapTokens: i provider denominano lo stesso modello in modo diverso, quindi copia l'id da lì.
Passo 2. Interrompi lo stream su richiesta#
Gli utenti premono Stop, chiudono una scheda o cambiano chat. Annulla la richiesta per non attendere token che nessuno leggerà. Passa un AbortSignal nelle opzioni della richiesta:
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 },
);Anche l'oggetto stream ha il proprio stream., e uscire dal ciclo for await con break chiude comunque la connessione. Usa quello più vicino al tuo codice: un signal quando il pulsante di annullamento si trova altrove, break quando la decisione viene presa all'interno del ciclo (ad esempio, hai trovato una stop word).
Passo 3. Gestisci gli errori#
Uno stream può fallire prima del primo token (chiave errata, saldo vuoto) o a metà (caduta di rete). Avvolgi sia la richiesta che il ciclo in un unico 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è ciò che ottieni dopo il tuoabort(). Non è un errore, quindi non mostrarlo come tale.APIErrorha l'statusHTTP: 401 significa chiave errata o mancante, 429 significa troppe richieste, e un messaggio sui token significa che il saldo deve essere ricaricato. Ulteriori cause e soluzioni sono in Common connection errors and how to fix them e "Not enough tokens" error.- Il testo già stampato rimane valido dopo un errore. Conservalo se vuoi mostrare la risposta parziale.
Passo 4. Leggi l'utilizzo e stima il costo#
L'oggetto usage contiene prompt_tokens e completion_. Insieme al prezzo del modello per 1M di token, ti dà il costo esatto di una richiesta. Per una stima approssimativa prima di eseguire qualsiasi cosa, usa la tabella live qui sotto: mostra il costo di 1.000 richieste con 2.000 token di input e 800 di output ciascuna.
| Modello | Input $/1M | Output $/1M | Per richiesta | 1000 richieste | OpenRouter, 1000 richieste |
|---|---|---|---|---|---|
| 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 |
2000 token di input e 800 token di output per richiesta. I prezzi per 1M di token sono attuali e cambiano insieme alle nostre tariffe; la colonna OpenRouter mostra i prezzi di riferimento per lo stesso carico di lavoro.
Per le risposte chat in streaming, MiniMax-M2.7 ha un prezzo di $0.05 per 1M di token di input e $0.15 per 1M di token di output, con una finestra di contesto di 180K e fino a 8.192 token di output per risposta. Il prezzo è fisso per modello a qualsiasi ora del giorno, e un unico saldo copre tutti i modelli, quindi cambiare la stringa model è sufficiente per confrontarli sui tuoi prompt.
Script completo#
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;
}
}Prossimi passi#
- Invia i chunk a un browser con Server-Sent Events dal tuo endpoint, così la chiave non lascia mai il server.
- Prova altri modelli cambiando solo la stringa
model: MiniMax-M2.7 è disponibile sullo stesso saldo e con lo stesso URL di base. - Ottieni una chiave ed esegui lo script: create a CheapTokens API key e ricarica il saldo.
Endpoint, id dei modelli e limiti per ogni modello sono nella CheapTokens docs.



