<h2>Entendendo Streams: A Base Conceitual</h2>
<p>Streams são sequências contínuas de dados que fluem através de sua aplicação em pequenos pedaços, em vez de carregar tudo em memória. Imagine um tubo por onde a água flui gradualmente—é exatamente assim que streams funcionam em Node.js. Essa abordagem é essencial para aplicações que precisam processar grandes volumes de dados (arquivos gigantes, uploads, logs em tempo real) sem consumir memória excessiva.</p>
<p>O modelo de streams em Node.js segue o padrão de Readable (leitura), Writable (escrita), Duplex (ambos) e Transform (transformação). Cada tipo oferece um nível de controle diferente sobre o fluxo de dados. A principal vantagem é o backpressure—o sistema sabe quando pausar e retomar o envio de dados automaticamente, evitando gargalos.</p>
<h2>Leitura de Dados com Streams Readable</h2>
<h3>Criando um Stream Readable</h3>
<p>A forma mais comum de ler arquivos é usar <code>fs.createReadStream()</code>. Este exemplo lê um arquivo em chunks de 64KB:</p>
<pre><code class="language-javascript">const fs = require('fs');
const readable = fs.createReadStream('arquivo-grande.txt', {
encoding: 'utf8',
highWaterMark: 64 * 1024 // 64KB por chunk
});
readable.on('data', (chunk) => {
console.log(Recebido chunk de ${chunk.length} bytes);
// Processa cada chunk
});
readable.on('end', () => {
console.log('Arquivo lido completamente');
});
readable.on('error', (err) => {
console.error('Erro na leitura:', err);
});</code></pre>
<h3>Controlando o Fluxo</h3>
<p>Você pode pausar e retomar streams manualmente. Isso é útil quando precisa controlar a taxa de consumo:</p>
<pre><code class="language-javascript">const readable = fs.createReadStream('dados.json');
readable.on('data', (chunk) => {
readable.pause();
setTimeout(() => {
console.log('Processando chunk...');
readable.resume();
}, 1000);
});</code></pre>
<h2>Escrita e Transformação de Dados</h2>
<h3>Streams Writable e Pipe</h3>
<p>O método <code>pipe()</code> conecta um stream readable diretamente a um writable, gerenciando automaticamente o backpressure:</p>
<pre><code class="language-javascript">const fs = require('fs');
const readable = fs.createReadStream('origem.txt');
const writable = fs.createWriteStream('destino.txt');
readable.pipe(writable);
writable.on('finish', () => {
console.log('Arquivo copiado com sucesso');
});</code></pre>
<h3>Transform Streams para Processamento</h3>
<p>Transform streams modificam dados enquanto fluem. Aqui convertemos texto para maiúsculas em tempo real:</p>
<pre><code class="language-javascript">const { Transform } = require('stream');
const fs = require('fs');
const maiusculasTransform = new Transform({
transform(chunk, encoding, callback) {
const maiorizado = chunk.toString().toUpperCase();
callback(null, maiorizado);
}
});
fs.createReadStream('entrada.txt')
.pipe(maiusculasTransform)
.pipe(fs.createWriteStream('saida.txt'));</code></pre>
<p>Para casos mais complexos, como parsear JSON line-delimited:</p>
<pre><code class="language-javascript">const { Transform } = require('stream');
const fs = require('fs');
const parseJSON = new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
try {
const linha = chunk.toString().trim();
if (linha) {
const obj = JSON.parse(linha);
callback(null, obj);
} else {
callback();
}
} catch (err) {
callback(err);
}
}
});
fs.createReadStream('dados.jsonl')
.pipe(parseJSON)
.on('data', (obj) => {
console.log('Objeto processado:', obj);
});</code></pre>
<h2>Padrões Avançados e Boas Práticas</h2>
<h3>Composição de Streams</h3>
<p>Combine múltiplos transforms para criar pipelines poderosos. Aqui filtramos e transformamos dados sequencialmente:</p>
<pre><code class="language-javascript">const { Transform } = require('stream');
const fs = require('fs');
const filtro = new Transform({
objectMode: true,
transform(obj, encoding, callback) {
if (obj.idade >= 18) {
callback(null, obj);
} else {
callback();
}
}
});
const formatador = new Transform({
objectMode: true,
transform(obj, encoding, callback) {
const formatado = ${obj.nome} (${obj.idade} anos)\n;
callback(null, formatado);
}
});
fs.createReadStream('usuarios.jsonl')
.pipe(parseJSON)
.pipe(filtro)
.pipe(formatador)
.pipe(fs.createWriteStream('maiores.txt'));</code></pre>
<h3>Tratamento de Erros</h3>
<p>Sempre implemente tratamento robusto de erros em pipelines:</p>
<pre><code class="language-javascript">const fs = require('fs');
const readable = fs.createReadStream('origem.txt');
const writable = fs.createWriteStream('destino.txt');
readable
.on('error', (err) => console.error('Erro na leitura:', err.message))
.pipe(writable)
.on('error', (err) => console.error('Erro na escrita:', err.message))
.on('finish', () => console.log('Pipeline concluído'));</code></pre>
<p>Ou use a sintaxe moderna com <code>pipeline()</code>:</p>
<pre><code class="language-javascript">const { pipeline } = require('stream');
const fs = require('fs');
pipeline(
fs.createReadStream('origem.txt'),
fs.createWriteStream('destino.txt'),
(err) => {
if (err) {
console.error('Erro no pipeline:', err.message);
} else {
console.log('Pipeline concluído com sucesso');
}
}
);</code></pre>
<h2>Conclusão</h2>
<p><strong>Streams são fundamentais</strong> em Node.js para trabalhar eficientemente com dados grandes. O trio Readable, Transform e Writable, combinado com <code>pipe()</code>, permite criar aplicações que processam informações de forma elegante e otimizada em memória.</p>
<p><strong>Sempre prefira streams</strong> sobre carregar tudo em buffer para manipulação de arquivos, uploads/downloads e processamento de dados em tempo real. Use <code>pipeline()</code> para melhor tratamento de erros em versões modernas do Node.js.</p>
<p><strong>Domine backpressure e composição</strong> de streams para construir arquiteturas robustas. Pequenas práticas—como definir <code>highWaterMark</code> apropriadamente e encadear transforms—fazem diferença significativa em produção.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://nodejs.org/api/stream.html" target="_blank" rel="noopener noreferrer">Documentação Oficial: Stream API do Node.js</a></li>
<li><a href="https://nodejs.org/en/docs/guides/backpressuring-in-streams" target="_blank" rel="noopener noreferrer">Node.js Best Practices: Streams</a></li>
<li><a href="https://www.nodejsdesignpatterns.com/" target="_blank" rel="noopener noreferrer">Readable Streams - Node.js Design Patterns</a></li>
<li><a href="https://github.com/substack/stream-handbook" target="_blank" rel="noopener noreferrer">Stream Handbook - Substack</a></li>
<li><a href="https://javascript.info/streams" target="_blank" rel="noopener noreferrer">JavaScript.info - Streams</a></li>
</ul>