Node.js Streams: Processing Data in Chunks, Not Blobs
Think of streams as a data conveyor belt, processing large files or network data in chunks instead of loading it all into memory. Use them for file I/O or network requests.
WHY IT EXISTS To handle large amounts of data efficiently. Loading a multi-gigabyte file or a continuous data feed entirely into memory is often impossible and always inefficient. Streams were created to process data piece by piece, keeping memory usage low and constant regardless of the total data size.
THE MENTAL MODEL Think of streams as a pipeline or a conveyor belt for data. Instead of waiting for a whole truckload of data to be delivered before you start working (loading a file into RAM), you process each item as it comes down the belt. This allows you to start processing immediately and use a fraction of the memory.
HOW IT WORKS Data flows from a source to a destination through a series of connected streams. There are four main types: Readable (the source, like a file), Writable (the destination, like an HTTP response), Duplex (both readable and writable, like a network socket), and Transform (a Duplex that modifies data as it passes through, like a zip compressor).
The .pipe() method connects the output of a Readable stream to the input of a Writable stream. It automatically manages the flow of data, including a critical concept called backpressure. If the writer is slow, it signals the reader to pause, preventing the writer from being overwhelmed and memory from filling up with buffered data. For more complex pipelines, stream.pipeline() is the modern, recommended approach as it properly handles error propagation and cleanup across all streams.
WHEN TO USE IT Use streams for any I/O-bound operation involving data that is large or arrives over time. This includes reading from or writing to the file system (fs.createReadStream), handling HTTP requests and responses, network communication, and data transformations like compression (zlib) or encryption (crypto).
WHEN NOT TO USE IT For small, self-contained data, the overhead of streams is unnecessary. Reading a small configuration file into memory with fs.readFileSync is simpler and perfectly acceptable. Streams are also not ideal when you need random access to the data, as they are designed for sequential processing.
ONE CANONICAL EXAMPLE Streaming a large video file to a client without loading the whole file on the server. The code fs.createReadStream('large-movie.mp4').pipe(response) connects a file read stream directly to an HTTP response stream. Node.js handles reading the file in chunks and writing those chunks to the response, using minimal memory. The client starts receiving video data almost instantly.
Read the original → nodejs.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.