Transform & Duplex
Streams Transform ficam no meio de pipelines para analisar, comprimir ou filtrar - Streams Duplex modelam canais bidirecionais como sockets com lados de leitura e escrita independentes.
Busque em todas as páginas da documentação
Streams Transform ficam no meio de pipelines para analisar, comprimir ou filtrar - Streams Duplex modelam canais bidirecionais como sockets com lados de leitura e escrita independentes.
import { Transform, PassThrough } from 'node:stream';
import { pipeline } from 'node:stream/promises';
const tap = new PassThrough();
tap.on('data', (chunk) => metrics.bytes(chunk.length));
await pipeline(source, tap, destination);Quando usar isso:
import { Transform, PassThrough, Duplex } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { Readable } from 'node:stream';
const splitLines = new Transform({
transform(chunk, _enc, cb) {
const parts = String(chunk).split('\n');
const tail = parts.pop() ?? '';
for (const line of parts) if (line) this.push(line);
cb(null, tail); // armazena linha incompleta no estado interno passando como chunk para o próximo
},
flush(cb) {
cb();
},
});
function tee<T extends NodeJS.ReadableStream>(source: T): [T, PassThrough] {
const branch = new PassThrough();
source.pipe(branch);
return [source, branch];
}
// Duplex conceitual - net.Socket estende Duplex
const socketLike = new Duplex({
read() { /* puxa do recurso subjacente */ },
write(chunk, _enc, cb) { cb(); /* envia para o par */ },
});import { createGzip } from 'node:zlib';
await pipeline(
Readable.from(['{"a":1}\n', '{"b":2}\n']),
splitLines,
createGzip(),
process.stdout,
);O que isso demonstra:
PassThrough é um Transform de identidade - ideal para canais laterais de tap/teezlib.createGzip() é um Transform internamenteread/write do Duplex são independentes - diferente do fluxo ligado do Transform_transform por chunk e _flush opcional no final._read e _write separadamente - sockets TCP, TLS.| Necessidade | Tipo |
|---|---|
| gzip, cifra | Transform |
| Socket TCP | Duplex |
| Métricas de tap | PassThrough |
| Parse de linhas | Transform |
import { Transform, type TransformCallback } from 'node:stream';
function createJsonParser(): Transform {
let buffer = '';
return new Transform({
objectMode: true,
transform(chunk: Buffer, _enc: BufferEncoding, cb: TransformCallback) {
buffer += chunk.toString('utf8');
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
try {
for (const line of lines) if (line) this.push(JSON.parse(line));
cb();
} catch (err) {
cb(err as Error);
}
},
});
}cb(err) uma vez._flush emite o restante.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
readline | Arquivos de texto baseados em linha | Frames binários |
| Gerador assíncrono map | Pequenas transformações em memória | Necessita de backpressure |
| Thread de worker dedicada | Trabalho pesado de CPU por chunk | Operações leves de string |
| Fanout de fila de mensagens | Múltiplos consumidores lentos | Apenas em processo |
A saída do Transform é derivada da entrada em um pipeline. Os lados de leitura/escrita do Duplex são canais independentes.
Monitoramento, branches tee, conexão de streams Web e Node com Duplex.fromWeb.
Dois branches PassThrough ou padrão multicast - observe o backpressure em ambos.
Use gerador assíncrono via Readable.from ou chame cb após await em _transform cuidadosamente uma vez.
Sim - createGzip, createGunzip são streams Transform.
pipeline(Readable.from([input]), transform, collectWritable) para asserir chunks de saída.
Executa quando o upstream termina - emite bytes/objetos em buffer pendentes.
Sim via pipeline - a ordem é fonte → parse → compressão → destino.
crypto.createCipheriv retorna Transform - mesmas regras de erro/pipeline.
Readable.fromWeb / Writable.toWeb conectam corpos de fetch e pipelines Node.
Use Transform de bytes antes/depois para converter - ex: bytes → objetos JSON → bytes.
Evite toString por chunk em buffers enormes - opere em fatias de Buffer quando possível.
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, e NestJS 11.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026