When building web applications with Node.js, we often need to read files, transfer payloads, or perform network data exchanges. Many developers default to functions like fs.readFile, which loads the entire file content into server RAM before returning it. While this is fine for small configurations, processing a 2GB CSV file or video buffer this way will cause Node's process memory limits to spike, resulting in server crashes. Streams solve this by processing data piece by piece.
Streams are collection logs of data—similar to arrays or string files—but with the crucial difference that they are processed in small chunks (usually 64KB buffers) instead of being loaded into memory all at once. In Node.js, there are four core stream classifications:
- [object Object]
To demonstrate the performance benefits, let's write a Node.js script that reads a huge log file, extracts specific string entries, compresses the output, and writes it to a new file:
import fs from 'fs';
import zlib from 'zlib';
import { Transform } from 'stream';
const readStream = fs.createReadStream('./logs/access.log', { encoding: 'utf8' });
const writeStream = fs.createWriteStream('./logs/filtered_errors.log.gz');
const gzip = zlib.createGzip();
// Transform stream to filter lines containing "ERROR"
const filterErrors = new Transform({
transform(chunk, encoding, callback) {
const lines = chunk.toString().split('
');
const filtered = lines.filter(line => line.includes('ERROR')).join('
');
this.push(filtered);
callback();
}
});
// Pipe the operations together
readStream
.pipe(filterErrors)
.pipe(gzip)
.pipe(writeStream)
.on('finish', () => {
console.log('File compressed and saved successfully!');
});
By using piping, Node.js buffers data internally. If the write stream is slower than the read stream, Node's backpressure system pauses the read stream automatically, keeping RAM consumption consistently low (often under 30MB).
Let's analyze memory footprints under load:
- [object Object]
Node.js streams are key for constructing highly scalable applications. By replacing memory-heavy buffer operations with clean readable, transform, and writable pipe chains, developers ensure their applications process heavy media files and request payloads reliably on minimal infrastructure.