API & SDKGuide
Diffuser des réponses de chat en Node.js avec le SDK OpenAI
Diffusez une complétion de chat jeton par jeton en Node.js : boucle for await, arrêt à la demande, gestion des erreurs et usage des jetons avec le SDK OpenAI.

Sur cette page
Le streaming permet à votre application d'afficher la réponse au fur et à mesure de sa génération au lieu d'attendre la réponse complète. Dans ce guide, vous allez créer un petit script Node.js qui diffuse une complétion de chat depuis l'API CheapTokens jeton par jeton, l'arrête à la demande et gère les erreurs. La version Python de ce sujet se trouve dans Streaming responses in Python.
Ce dont vous avez besoin#
- Node.js 22 ou plus récent (la version minimale indiquée dans le README du paquet
openai, vérifiée le 22 octobre 2026). - Une clé API CheapTokens. Si vous n'en avez pas encore, suivez Your first API request with CheapTokens.
- Le SDK officiel :
npm install openai.
CheapTokens expose une API compatible OpenAI à l'adresse https:/, donc le paquet officiel openai fonctionne en changeant uniquement l'URL de base et la clé. Gardez la clé dans une variable d'environnement, pas dans le code :
export CHEAPTOKENS_API_KEY="your-key"Étape 1. Diffuser la réponse avec for await#
Créez stream.mjs (l'extension .mjs vous permet d'utiliser import et await au niveau supérieur) :
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);Exécutez-le avec node stream.mjs. Le SDK lit les Server-Sent Events de l'API et vous fournit un chunk par itération de for await. Trois détails comptent :
- Le texte arrive dans
chunk.. Certains chunks ne contiennent pas de texte (le premier ne contient souvent que le rôle), donc vérifiez quechoices[0]. delta. content deltan'est pas vide. process.s'affiche sans saut de ligne, donc les mots apparaissent les uns après les autres.stdout. write - Avec
stream_, le dernier chunk contient un objetoptions: { include_ usage: true } usageavec les nombres de jetons de prompt et de complétion, et son tableauchoicesest vide. C'est pourquoi le code utilisechunk.avec le chaînage optionnel.choices[0]?.
L'identifiant du modèle provient de la documentation CheapTokens : les fournisseurs nomment différemment le même modèle, donc copiez l'identifiant depuis là.
Étape 2. Arrêter le flux à la demande#
Les utilisateurs cliquent sur Stop, ferment un onglet ou changent de chat. Annulez la requête pour ne pas attendre des jetons que personne ne lira. Passez un AbortSignal dans les options de la requête :
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 },
);L'objet stream possède aussi son propre stream., et sortir de la boucle for await avec break ferme également la connexion. Utilisez ce qui est le plus proche de votre code : un signal quand le bouton d'annulation se trouve ailleurs, break quand la décision est prise à l'intérieur de la boucle (par exemple, vous avez trouvé un mot d'arrêt).
Étape 3. Gérer les erreurs#
Un flux peut échouer avant le premier jeton (mauvaise clé, solde vide) ou en cours de route (coupure réseau). Enveloppez à la fois la requête et la boucle dans un seul 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;
}
}APIUserAbortErrorest ce que vous obtenez après votre propreabort(). Ce n'est pas un échec, donc ne l'affichez pas comme tel.APIErrorcontient lestatusHTTP : 401 signifie une clé erronée ou manquante, 429 signifie trop de requêtes, et un message sur les jetons signifie que le solde doit être rechargé. D'autres causes et solutions se trouvent dans Common connection errors and how to fix them et "Not enough tokens" error.- Le texte déjà affiché reste valide après une erreur. Conservez-le si vous voulez afficher la réponse partielle.
Étape 4. Lire l'usage et estimer le coût#
L'objet usage contient prompt_tokens et completion_. Avec le prix du modèle par 1M de jetons, il vous donne le coût exact d'une requête. Pour une estimation approximative avant d'exécuter quoi que ce soit, utilisez le tableau en direct ci-dessous : il montre le coût de 1 000 requêtes avec 2 000 jetons d'entrée et 800 jetons de sortie chacune.
| Modèle | Entrée $/1M | Sortie $/1M | Par requête | 1 000 requêtes | OpenRouter, 1 000 requêtes |
|---|---|---|---|---|---|
| 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 tokens d'entrée et 800 tokens de sortie par requête. Les prix par million de tokens sont actuels et évoluent avec nos tarifs ; la colonne OpenRouter indique des prix de référence pour la même charge de travail.
Pour les réponses de chat diffusées, MiniMax-M2.7 est facturé à $0.05 par 1M de jetons d'entrée et $0.15 par 1M de jetons de sortie, avec un contexte de 180K et jusqu'à 8 192 jetons de sortie par réponse. Le prix est fixe par modèle à tout moment de la journée, et un seul solde couvre tous les modèles, donc changer la chaîne model suffit pour les comparer sur vos propres prompts.
Script complet#
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;
}
}Étapes suivantes#
- Envoyez les chunks à un navigateur avec Server-Sent Events depuis votre propre endpoint, afin que la clé ne quitte jamais le serveur.
- Essayez d'autres modèles en changeant uniquement la chaîne
model: MiniMax-M2.7 est disponible sur le même solde et la même URL de base. - Obtenez une clé et exécutez le script : create a CheapTokens API key et rechargez le solde.
Les endpoints, identifiants de modèles et limites de chaque modèle se trouvent dans la CheapTokens docs.



