Écrire un export avec contrôle de flux
Objectif : Relier lecture, transformation et écriture en respectant le débit des composants.
Un export peut contenir bien plus de données que votre processus ne peut en conserver raisonnablement en mémoire. Même si la lecture et la conversion se font par flux, une destination plus lente peut laisser les données s’accumuler si vous ne respectez pas son rythme.
Vous allez connecter les commandes déjà lues par lots au transformateur CSV, puis écrire le résultat dans un fichier. Le mécanisme de contrôle de flux permet de ralentir la lecture lorsque la destination n’est plus prête à recevoir des données.
La destination d’écriture et son débit
Un flux inscriptible, ou Writable, est une destination qui accepte des morceaux de données successifs. Un fichier ouvert avec le module Node.js fs en fournit un exemple. Sa méthode write renvoie false lorsque le tampon interne a atteint son seuil : le producteur doit alors attendre l’événement drain avant de reprendre.
Ce mécanisme est la contre-pression, ou backpressure : la destination lente indique aux flux en amont de ralentir. Vous n’avez pas à implémenter vous-même cette coordination pour un pipeline Node.js standard. La fonction pipeline relie les flux, gère cette contre-pression et transmet les erreurs à son rappel de fin.
Composer le parcours, le CSV et le fichier
Vous disposez déjà, dans src/services/exports.js, de creerFluxCommandes() et creerFluxCsv(). Le premier produit les commandes du dépôt injecté et le second les encode en CSV ; la composition ci-dessous ajoute un fichier comme destination. Le dépôt doit continuer de respecter le contrat établi dans la leçon sur la lecture par lots.
const fs = require('node:fs');
const path = require('node:path');
const { pipeline } = require('node:stream/promises');
const { creerServiceExports } = require('./src/services/exports');
const depot = {
async lireLot({ apresId, limite }) {
// Remplacez ce tableau par une lecture paginée en base.
const commandes = [
{ id: 'cmd-001', clientId: 'cli-01', statut: 'payee', total: 42.5 },
{ id: 'cmd-002', clientId: 'cli-02', statut: 'en_attente', total: 18 }
];
return commandes
.filter((commande) => apresId === null || commande.id > apresId)
.slice(0, limite);
}
};
async function exporterCommandes({ serviceExports, destination }) {
await pipeline(
serviceExports.creerFluxCommandes(),
serviceExports.creerFluxCsv(),
fs.createWriteStream(destination)
);
}
async function main() {
const serviceExports = creerServiceExports(depot, 100);
const destination = path.join(__dirname, 'commandes.csv');
await exporterCommandes({ serviceExports, destination });
console.log(`Export terminé : ${destination}`);
}
main().catch((erreur) => {
console.error('Échec de l’export :', erreur.message);
process.exitCode = 1;
});Le fichier commandes.csv contient une ligne d’en-tête puis les commandes en CSV. La console affiche « Export terminé : …/commandes.csv » si le pipeline aboutit. En cas d’erreur de lecture, de transformation ou d’écriture, l’export échoue et le processus termine avec un code non nul.Le dépôt de démonstration renvoie deux commandes pour que le programme soit exécutable sans base de données. En production, remplacez uniquement cette implémentation par votre dépôt réel ; conservez l’injection et l’ordre des flux. Le fichier est créé par un flux d’écriture, sans constituer une chaîne contenant tout l’export.
Vérifier le résultat et traiter les erreurs
Avec les données de démonstration, le fichier produit contient l’en-tête id,clientId,statut,total, puis les lignes cmd-001,cli-01,payee,42.5 et cmd-002,cli-02,en_attente,18. Les caractères spéciaux dans les champs restent traités par le transformateur CSV existant. L’appel à pipeline ne se termine avec succès qu’une fois l’écriture achevée.
Ici, le rejet est récupéré dans main().catch : l’erreur est signalée et process.exitCode indique un échec au système. Dans un serveur, vous traiterez ce rejet au niveau de la requête concernée plutôt que de terminer le processus. La leçon suivante abordera l’envoi HTTP, l’interruption d’un export et le nettoyage associé.
À retenir
- Un
Writablereçoit des données par morceaux et signale lorsqu’il faut ralentir. - La contre-pression évite que les flux amont débordent le tampon d’une destination lente.
pipelinerelie les flux, coordonne leur débit et propage les erreurs.- L’écriture en flux évite de conserver l’ensemble du fichier d’export en mémoire.