- Node.js Tutorial
- NodeJS Home
- NodeJS Introduction
- NodeJS Setup
- NodeJS First App
- NodeJS REPL
- NodeJS Command Line
- NodeJS NPM
- NodeJS Callbacks
- NodeJS Events
- NodeJS Event-Loop
- NodeJS Event-Emitter
- NodeJS Global-Objects
- NodeJS Console
- NodeJS Process
- NodeJS Buffers
- NodeJS Streams
- Node.js File Handling
- Node.js File System
- Node.js Read/Write File
- Working with folders in Node.js
- HTTP and Networking
- Node.js HTTP Module
- Anatomy of an HTTP Transaction
- Node.js MongoDB
- MongoDB Get Started
- MongoDB Create Database
- MongoDB Create Collection
- MongoDB Insert
- MongoDB Find
- MongoDB Query
- MongoDB Sort
- MongoDB Delete
- MongoDB Update
- MongoDB Limit
- MongoDB Join
- Node.js MySQL
- MySQL Get Started
- MySQL Create Database
- MySQL Create Table
- MySQL Insert Into
- MySQL Select From
- MySQL Where
- MySQL Order By
- MySQL Delete
- MySQL Update
- MySQL Join
- Node.js Modules
- Node.js Modules
- Node.js Built-in Modules
- Node.js Utility Modules
- Node.js Web Module
- Node.js Advanced
- Node.js Debugger
- Node.js Scaling Application
- Node.js Packaging
- Node.js Express Framework
- Node.js RESTFul API
- Node.js Useful Resources
- Node.js Useful Resources
- Node.js Discussion
Node.js Streams
Streams are one of the most powerful, yet often misunderstood, features in Node.js. In simple terms, a stream is an interface for handling data like reading a file or communicating with a remote server bit by bit. Instead of loading an entire 2GB file into your RAM (which would likely crash your app), streams allow you to process that data in small "chunks."
Key Features of Streams
- Memory Efficiency: You don't need to load large amounts of data into memory before you can process it. This allows you to handle files larger than your available RAM.
- Time Efficiency: You can start processing data as soon as you receive the first chunk, rather than waiting for the entire payload to arrive.
- Composable: You can "chain" streams together to create complex data processing pipelines, keeping your code modular and clean.
fs.readFile() for everything. While fine for small config files, using it for large logs or uploads will cause your application's memory usage to spike and potentially crash under heavy load.
Types of Streams
1. Readable Streams
A Readable stream is a source from which data is consumed. Node.js provides many built-in readable streams, such as fs.createReadStream for files or req (the request object) in an HTTP server.
const fs = require('fs');
// We create a stream to read a large log file
const readableStream = fs.createReadStream('access.log', { encoding: 'utf8', highWaterMark: 16 * 1024 });
readableStream.on('data', (chunk) => {
console.log(`--- New Chunk Received (${chunk.length} characters) ---`);
console.log(chunk);
});
readableStream.on('end', () => {
console.log('Finished reading the entire file.');
});
readableStream.on('error', (err) => {
console.error('An error occurred:', err.message);
});
- The
dataevent triggers every time a new piece of data is ready for you to use. - The
highWaterMarkproperty (optional) determines the size of the chunks (default is 64KB for readable streams). - The
endevent tells you there is nothing left to read.
2. Writable Streams
A Writable stream is a destination where you can send data. Examples include writing to a file, sending an HTTP response (res), or writing to the terminal (process.stdout).
const fs = require('fs');
const writableStream = fs.createWriteStream('output.txt');
writableStream.write('First line of data.\n');
writableStream.write('Second line of data.\n');
// Always signal that you are done writing
writableStream.end('Finalizing the stream.');
writableStream.on('finish', () => {
console.log('All data has been flushed to the file system.');
});
write() if it returns false, you should wait for the drain event before writing more.
3. Duplex Streams
Duplex streams are hybrid streams that implement both Readable and Writable interfaces. A classic real-world example is a Network Socket. You can send data to a server (Write) and receive data back from it (Read) over the same connection.
const net = require('net');
const server = net.createServer((socket) => {
// 'socket' is a duplex stream
socket.write('Server: Connection established.\n');
socket.on('data', (data) => {
console.log(`Received from client: ${data.toString()}`);
});
});
server.listen(8080);
4. Transform Streams
Transform streams are a special type of Duplex stream where the output is computed from the input. You use them to modify data as it passes through. Common uses include data compression, encryption, or converting CSV data to JSON.
const zlib = require('zlib');
const fs = require('fs');
const gzip = zlib.createGzip(); // This is a Transform stream
const input = fs.createReadStream('bigfile.txt');
const output = fs.createWriteStream('bigfile.txt.gz');
// The data flows: File -> Gzip Compression -> Compressed File
input.pipe(gzip).pipe(output);
Stream Methods and Properties
1. pipe()
The pipe() method is the most common way to consume streams. It connects the output of a Readable stream directly into the input of a Writable stream, automatically managing the flow so the destination isn't overwhelmed.
const fs = require('fs');
const src = fs.createReadStream('source.zip');
const dest = fs.createWriteStream('copy.zip');
src.pipe(dest);
pipe() is convenient, it doesn't automatically destroy streams if an error occurs, which can lead to memory leaks. In modern Node.js, it is recommended to use the pipeline() function from the stream module instead, as it handles cleanup properly.
2. setEncoding()
By default, streams return data as Buffer objects (raw binary). If you are working with text, setEncoding() tells the stream to automatically convert those buffers into strings.
const readableStream = fs.createReadStream('notes.txt');
readableStream.setEncoding('utf8');
readableStream.on('data', (chunk) => {
// 'chunk' is now a string, not a Buffer
console.log(chunk);
});
3. pause() and resume()
Sometimes you need to "pause" the data flow for example, if you are reading data from a file but need to wait for a database to finish an operation before processing the next chunk.
const readable = fs.createReadStream('data.json');
readable.on('data', (chunk) => {
console.log('Processing chunk...');
readable.pause(); // Stop receiving data
setTimeout(() => {
console.log('Resuming...');
readable.resume(); // Start receiving data again after 1 second
}, 1000);
});
Summary
Streams are the backbone of high-performance Node.js applications. By breaking data into chunks, they allow your server to remain responsive and low on memory usage even when handling massive files or heavy network traffic. Master the use of Readable and Writable streams first, and then explore Transform streams to build powerful data-processing pipelines. Whether you're building a video streaming service, a log analyzer, or a file uploader, streams are your best friend for writing scalable code.