gf
gf
js
Streams in Node.js
Part 2: Types & Advanced Operations
March 28, 2025
Source Code
NODE.JS STREAMS
1. Types of Streams
Node.js provides four fundamental types of streams:
• Readable: Sources from which data can be read (files, HTTP requests)
• Writable: Destinations to which data can be written (files, HTTP responses)
• Duplex: Both readable and writable (TCP sockets)
• Transform: Modify data as it passes through (compression, encryption)
14
15 // Transform Stream
16 const { Transform } = require("stream");
17 new Transform({
18 transform(chunk, encoding, callback) {
19 // Convert data to uppercase
20 callback(null, chunk.toString().toUpperCase());
21 },
22 });
2. Stream Operations
2.1. The Pipe Method
The most powerful way to connect streams:
1 const fs = require(’fs’);
2 const zlib = require(’zlib’);
3
4 // Creating a pipeline using pipe()
5 fs.createReadStream(’file.txt’)
6 .pipe(zlib.createGzip())
7 .pipe(fs.createWriteStream(’file.txt.gz’))
8 .on(’finish’, () => {
9 console.log(’Compression completed’);
10 });
1 const fs = require(’fs’);
2 const csv = require(’csv-parser’);
3
4 // Process a large CSV file line by line
5 fs.createReadStream(’huge-data.csv’)
6 .pipe(csv())
7 .on(’data’, (row) => {
8 // Process each row without loading the entire file
9 console.log(row);
10 })
11 .on(’end’, () => {
12 console.log(’Processing complete’);
13 });
4. Best Practices
4.1. Managing Backpressure
Prevent memory overflow when reading faster than writing:
1 const fs = require(’fs’);
2
3 const readableStream = fs.createReadStream(’large-file.dat’);
4 const writableStream = fs.createWriteStream(’destination.dat’);
5
6 readableStream.on(’data’, (chunk) => {
7 // write() returns false when internal buffer is full
8 const canWrite = writableStream.write(chunk);
9
10 if (!canWrite) {
11 // Pause the readable stream until the writable drains
12 readableStream.pause();
13
14 // Resume when the writable can accept more data
15 writableStream.once(’drain’, () => {
16 readableStream.resume();
17 });
18 }
19 });
20
21 readableStream.on(’end’, () => {
22 writableStream.end();
23 });
17 this.incomplete = lines.pop();
18
19 // Filter and send lines containing the keyword
20 for (const line of lines) {
21 if (line.includes(this.keyword)) {
22 this.push(line + ’\n’);
23 }
24 }
25 callback();
26 }
27
28 _flush(callback) {
29 // Process any remaining data
30 if (this.incomplete && this.incomplete.includes(this.keyword)) {
31 this.push(this.incomplete + ’\n’);
32 }
33 callback();
34 }
35 }
5. Conclusions
5.1. Powerful Data Processing Pipelines
Node.js streams provide a versatile framework for building efficient data processing pipelines. By con-
necting different stream types through piping or the pipeline API, developers can create complex data
workflows that process information incrementally. This architecture naturally fits many real-world prob-
lems, from ETL processes to real-time data transformations. The ability to compose streams together
like building blocks makes it possible to create maintainable solutions that can evolve with changing
requirements.
6. References
• Node.js. (2023). Stream | Node.js v18.x Documentation. Link
• Alapont, R. (2023). Streamlining Your Code: Best Practices for Node.js Streams. Link