Ir al contenido
angel.library

Streams

Un stream procesa datos en pedazos (chunks) a medida que llegan, en vez de esperar a tener todo el contenido en memoria antes de empezar. Para un archivo de 2GB, la diferencia es literal entre “usa 2GB de RAM” y “usa unos pocos KB, sin importar el tamaño del archivo”.

Readable → de donde se leen datos (un archivo, una request HTTP entrante)
Writable → a donde se escriben datos (un archivo, una response HTTP)
Duplex → los dos a la vez (un socket TCP)
Transform → Duplex que además modifica los datos que pasan (compresión, encriptado)
import { createReadStream } from "node:fs"
const stream = createReadStream("archivo-grande.csv", { encoding: "utf-8" })
stream.on("data", (chunk) => {
console.log("Pedazo recibido:", chunk.length, "bytes")
})
stream.on("end", () => {
console.log("Terminó de leer todo")
})
stream.on("error", (err) => {
console.error("Algo falló:", err)
})

Comparar con readFile (de Filesystem): ese carga el archivo completo en memoria antes de devolver algo — para un archivo de unos KB no importa, para uno de varios GB puede tirar el proceso por falta de memoria.

import { createReadStream, createWriteStream } from "node:fs"
createReadStream("origen.txt").pipe(createWriteStream("copia.txt"))

.pipe() maneja automáticamente el backpressure: si el destino (Writable) es más lento consumiendo que el origen (Readable) produciendo, pausa la lectura hasta que el destino esté listo para más — sin esto, un stream rápido escribiendo a uno lento acumularía todo en memoria de todas formas, perdiendo la ventaja de usar streams en primer lugar.

req y res en un servidor Node son streams (req es Readable y res es Writable). Por eso, en el servidor HTTP nativo, leer el body de una request implica escuchar data y end en vez de recibirlo ya construido.

import { createReadStream } from "node:fs"
import { createServer } from "node:http"
const server = createServer((req, res) => {
if (req.url === "/descargar") {
// el archivo se transmite en pedazos directo a la respuesta,
// sin cargarlo entero en memoria del servidor
createReadStream("archivo-grande.zip").pipe(res)
}
})
ConceptoQué es
ReadableFuente de datos, en pedazos (data/end/error)
WritableDestino de datos
.pipe(destino)Conecta ambos, maneja backpressure automáticamente
BackpressureEl mecanismo que evita que un productor rápido sature a un consumidor lento
  • Para la mayoría del código de aplicación (leer un archivo de config chico, un JSON pequeño), readFile/writeFile normal es más simple y perfectamente adecuado — streams importan específicamente cuando el tamaño de los datos es grande o desconocido de antemano (archivos, uploads, respuestas HTTP grandes).
  • req.on('data', ...) consume manualmente un stream Readable; frameworks como Express encapsulan ese trabajo con middleware de parsing.