Node.js I/O

Node.js Streams

Los streams procesan los datos en fragmentos (chunks) en lugar de cargarlos todos en memoria. Así es como Node copia archivos, comprime payloads y canaliza cuerpos HTTP de manera eficiente.

intermediate15 min readUpdated 15 sept 2026
compress.js
js
// compress.js
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { createGzip } from "node:zlib";

await pipeline(
  createReadStream("access.log"),
  createGzip(),
  createWriteStream("access.log.gz"),
);
Tamaño del fragmento
highWaterMark
Tipos
Readable, Writable, Duplex, Transform
Conexión
pipe y pipeline
Control de flujo
Backpressure
Iteración
for await
Ideal para
Datos grandes o ilimitados

Por que importa

Por qué los streams son importantes

Memoria constante

Un stream mantiene un solo fragmento a la vez, por lo que puedes procesar gigabytes con unos pocos megabytes de memoria.

Backpressure natural

Un consumidor lento indica al productor que reduzca la velocidad, evitando que las fuentes rápidas saturen la memoria o un disco lento.

Pipelines componibles

Las etapas Readable, transform y writable se ensamblan fácilmente, convirtiendo procesos complejos en cadenas legibles.

La imagen completa

Las tres ideas detrás de los streams

Los datos fluyen en fragmentos, la backpressure mantiene el equilibrio entre productor y consumidor, y el pipeline conecta las etapas.

Chunks

Flujo

Los datos se mueven en buffers u objetos en lugar de un único valor masivo.

Backpressure

Equilibrio

Los buffers internos y el highWaterMark evitan que los productores superen la capacidad de los consumidores.

Pipeline

Composición

pipeline conecta las etapas y propaga los errores y la limpieza.

Streams de un vistazo

El núcleo de los streams

Readable

Una fuente de la cual lees, como un archivo, un socket o el cuerpo de una solicitud.

Writable

Un destino donde escribes, como un archivo o una respuesta.

Duplex

Tanto readable como writable, como un socket TCP.

Transform

Una etapa duplex que modifica los datos, como gzip o un parser.

Backpressure

write devuelve false y esperas al evento drain.

Object mode

Transmite objetos en lugar de bytes para pipelines estructurados.

Una breve historia

Evolución de los streams

  1. 2010

    Llegan los streams

    Node introduce los streams para manejar datos de forma incremental.

    10
  2. 2012

    Streams 2

    Una API rediseñada añade pipe y backpressure.

    12
  3. 2017

    Streams 3

    Semántica más limpia y pipeline mejoran el manejo de errores.

    17
  4. 2018

    Iteración asíncrona

    for await of hace que leer streams se sienta como leer arrays.

    18
  5. Hoy

    En todas partes

    HTTP, archivos, compresión, crypto y muchas librerías se basan en streams.

    Hoy

La guia completa

Node.js Streams: Todo lo que necesitas saber

Por qué los streams son importantes

Cargar un archivo grande en memoria funciona hasta que el archivo es más grande que la memoria disponible. Los streams solucionan esto procesando los datos en fragmentos (chunks): lees una parte, la procesas y continúas, de modo que el uso de memoria se mantiene prácticamente constante sin importar cuán grande sea la entrada.

Los streams están en todas partes en Node.js. Los cuerpos de las solicitudes y respuestas HTTP, la lectura y escritura de archivos, la compresión, el cifrado y muchos parsers son streams. Comprenderlos es lo que te permite construir servidores y herramientas capaces de manejar datos ilimitados sin que el sistema colapse.

Los cuatro tipos de streams

Cada stream pertenece a una de estas cuatro categorías:

  • Readable — una fuente de la cual se lee. Archivos, cuerpos de peticiones HTTP, sockets y process.stdin.
  • Writable — un destino donde se escribe. Archivos, respuestas HTTP, sockets y process.stdout.
  • Duplex — tanto readable como writable, como un socket TCP.
  • Transform — un stream duplex que modifica los datos a medida que pasan, como zlib.createGzip() o un parser de CSV.

Los streams readable y writable se conectan mediante pipelines, y los transforms se ubican en medio de ellos.

Lectura y escritura

La forma más sencilla de consumir un readable stream es mediante la iteración asíncrona.

// read.js
import { createReadStream } from "node:fs";

const stream = createReadStream("access.log", { encoding: "utf8" });

for await (const chunk of stream) {
  process.stdout.write(chunk);
}

Para los writable streams, llama a write() y señala el final con end().

// write.js
import { createWriteStream } from "node:fs";

const out = createWriteStream("out.txt");

out.write("first line\n");
out.write("second line\n");
out.end();

El stream almacena las escrituras en un buffer interno y las vuelca de manera eficiente, por lo que no tienes que gestionar los chunks tú mismo.

Piping y pipeline

Un pipeline conecta un readable, cero o más transforms y un writable.

// compress.js
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { createGzip } from "node:zlib";

await pipeline(
  createReadStream("access.log"),
  createGzip(),
  createWriteStream("access.log.gz"),
);

Prefiere siempre pipeline sobre el encadenamiento manual de .pipe(). pipeline reenvía los errores de cada etapa, destruye los streams en caso de fallo y devuelve una promesa, lo que simplifica el manejo de errores. El .pipe() manual no propaga los errores, por lo que un fallo upstream puede dejar el pipeline colgado y el destino abierto.

Backpressure

Los streams tienen buffers internos. Cuando escribes más rápido de lo que el destino puede consumir, el buffer se llena y write() devuelve false.

// backpressure.js
function writeAll(stream, chunks) {
  return new Promise((resolve, reject) => {
    let i = 0;
    const next = () => {
      while (i < chunks.length) {
        const ok = stream.write(chunks[i++]);
        if (!ok) {
          stream.once("drain", next);
          return;
        }
      }
      stream.end(resolve);
    };
    stream.on("error", reject);
    next();
  });
}

Esperar a drain antes de escribir más es la forma en que un productor respeta a un consumidor lento. pipeline y pipe gestionan esto automáticamente, lo cual es otra razón más para utilizarlos.

Transform streams

Un transform stream aplica una función a cada chunk. Puedes construir los tuyos propios.

// upper.js
import { Transform } from "node:stream";

const upper = new Transform({
  transform(chunk, encoding, callback) {
    callback(null, chunk.toString().toUpperCase());
  },
});

process.stdin.pipe(upper).pipe(process.stdout);

Node incluye transforms útiles en node:zlib (gzip, deflate), node:crypto (ciphers y hashes) y en muchas librerías, como parsers de CSV y JSON.

Modo objeto

Por defecto, los streams transportan bytes. El modo objeto permite que transporten objetos JavaScript en su lugar, lo cual es ideal para pipelines estructurados.

// object-mode.js
import { Readable, Transform } from "node:stream";

Readable.from([{ id: 1 }, { id: 2 }])
  .pipe(
    new Transform({
      objectMode: true,
      transform(record, _encoding, callback) {
        callback(null, { ...record, seen: true });
      },
    }),
  )
  .on("data", console.log);

En modo objeto, highWaterMark cuenta objetos en lugar de bytes. Así es como se construyen los streams de filas de bases de datos, los procesadores de logs y los pipelines de ETL.

Patrones comunes

  • Copiar un archivo: pipeline(createReadStream(src), createWriteStream(dest)).
  • Comprimir o cifrar: insertar createGzip() o un transform de cifrado.
  • Transmitir una respuesta HTTP: redirigir (pipe) un archivo o el resultado de una consulta directamente a res.
  • Parsear datos delimitados por líneas: usar un transform que divida el contenido por saltos de línea.
  • Progreso de subida: contar los bytes en un transform a medida que pasan.

Mejores prácticas

  • Prefiere pipeline sobre .pipe() para el manejo de errores y la limpieza.
  • Transmite archivos y respuestas grandes mediante streams en lugar de almacenarlos en búfer.
  • Respeta la contrapresión (backpressure); nunca ignores cuando write() devuelva false.
  • Ajusta highWaterMark solo después de realizar mediciones.
  • Usa el modo de objeto para datos estructurados.
  • Gestiona error en cada stream que crees.
  • Destruye los streams en caso de fallo para que se liberen los descriptores de archivo.

Errores comunes

  • Usar readFile para datos extensos o sin límite.
  • Encadenar .pipe() y perder los errores.
  • Ignorar el backpressure y almacenar en búfer datos sin límite.
  • Olvidar que un callback de transform debe llamarse exactamente una vez.
  • Mezclar codificaciones y generar una salida corrupta.
  • Dejar streams abiertos después de un error.

Próximos pasos

Los streams son la forma en que Node maneja datos a escala. Ponlos en práctica con la guía del File System y comprende la programación subyacente en la guía del Event Loop. Después, reescribe una llamada a readFile como un stream y observa cómo se estabiliza el uso de memoria.

Conexión de etapas de stream

pipeline gestiona los errores, cierra cada etapa y realiza la limpieza. El pipe manual no reenvía errores, por lo que los fallos pueden bloquear el proceso o causar fugas de memoria.

Preferir
import { pipeline } from "node:stream/promises";

await pipeline(source, transform, destination);
Evitar
source.pipe(transform).pipe(destination);
// errors on source are not
// forwarded to destination

Lectura de un archivo grande

Un stream mantiene el uso de memoria estable. readFile carga todo el archivo en el buffer, lo cual está bien para archivos pequeños pero es peligroso para los grandes.

Preferir
import { createReadStream } from "node:fs";

const stream = createReadStream("big.log");
for await (const chunk of stream) {
  handle(chunk);
}
Evitar
import { readFile } from "node:fs/promises";

// the whole file in memory
const data = await readFile("big.log");
handle(data);

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Streams?

Nuestro tutorial interactivo te guia a traves de Streams paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.