Introduction
Streams are an important Node.js concept for working with data piece by piece instead of loading everything into memory at once. They are useful for large files, HTTP requests, uploads, downloads, and real-time data. In this chapter, you will practice readable, writable, duplex, and transform streams, along with pipe(), stream events, file streams, and practical examples. The questions start from simple concepts and gradually move toward real-world Node.js usage. Node.js Streams practice questions with solutions help to understand the concepts.
Question 1: What is a Readable Stream in Node.js?
Problem
Create a simple readable stream and read data from it.
Solution
const { Readable } = require("stream");
const readableStream = Readable.from([
"Hello ",
"from ",
"Node.js ",
"Streams!"
]);
readableStream.on("data", (chunk) => {
console.log("Received:", chunk.toString());
});
readableStream.on("end", () => {
console.log("Reading completed.");
});
Output
Received: Hello
Received: from
Received: Node.js
Received: Streams!
Reading completed.
Step-by-Step Explanation
A readable stream is used when Node.js needs to read data.
We import Readable:
const { Readable } = require("stream");
Then create a readable stream:
const readableStream = Readable.from([
"Hello ",
"from ",
"Node.js ",
"Streams!"
]);
The data event is triggered when data is available:
readableStream.on("data", (chunk) => {
console.log(chunk.toString());
});
When there is no more data, the end event runs:
readableStream.on("end", () => {
console.log("Reading completed.");
});
Question 2: How do you create a Writable Stream?
Problem
Create a writable stream that receives data and displays it on the console.
Solution
const { Writable } = require("stream");
const writableStream = new Writable({
write(chunk, encoding, callback) {
console.log(
"Received:",
chunk.toString()
);
callback();
}
});
writableStream.write("Hello ");
writableStream.write("Node.js ");
writableStream.write("Streams!");
writableStream.end();
Output
Received: Hello
Received: Node.js
Received: Streams!
Step-by-Step Explanation
A writable stream is used when an application needs to write data.
We create a writable stream:
const writableStream = new Writable({
write(chunk, encoding, callback) {
// Handle data
}
});
The write() method receives the data.
The data arrives as a Buffer by default, so we convert it to text:
chunk.toString()
After processing the chunk, we call:
callback();
This tells the stream that the current write operation has completed.
Question 3: How do you read a file using a Readable Stream?
Problem
Read a text file using fs.createReadStream() and display its contents chunk by chunk.
Solution
First create message.txt:
Welcome to Node.js.
Streams allow us to process data in chunks.
Create index.js:
const fs = require("fs");
const readStream = fs.createReadStream(
"message.txt",
"utf8"
);
readStream.on("data", (chunk) => {
console.log("Received chunk:");
console.log(chunk);
});
readStream.on("end", () => {
console.log("File reading completed.");
});
readStream.on("error", (error) => {
console.log("Error:", error.message);
});
Output
The output depends on the file size and stream buffering, but it will contain the file’s text followed by:
File reading completed.
Step-by-Step Explanation
fs.createReadStream() creates a readable stream for a file:
const readStream = fs.createReadStream(
"message.txt",
"utf8"
);
The data event receives chunks:
readStream.on("data", (chunk) => {
console.log(chunk);
});
The end event runs after the entire file has been read:
readStream.on("end", () => {
console.log("File reading completed.");
});
The error event handles problems such as a missing file.
Question 4: How do you write data using a Writable File Stream?
Problem
Create a file and write multiple pieces of data using fs.createWriteStream().
Solution
const fs = require("fs");
const writeStream = fs.createWriteStream(
"output.txt"
);
writeStream.write("Hello Node.js!\n");
writeStream.write("This is a writable stream.\n");
writeStream.write("Data is being written in chunks.\n");
writeStream.end();
writeStream.on("finish", () => {
console.log("File writing completed.");
});
writeStream.on("error", (error) => {
console.log("Error:", error.message);
});
Output
Console:
File writing completed.
A new file named output.txt is created:
Hello Node.js!
This is a writable stream.
Data is being written in chunks.
Step-by-Step Explanation
We create a writable file stream:
const writeStream = fs.createWriteStream(
"output.txt"
);
Then write data:
writeStream.write("Hello Node.js!\n");
We can call .write() multiple times.
When we are finished:
writeStream.end();
The finish event runs when all data has been flushed to the underlying system:
writeStream.on("finish", () => {
console.log("File writing completed.");
});
Question 5: How do you copy a large file using pipe()?
Problem
Copy one file to another using streams and pipe().
Solution
Create source.txt:
This is the source file.
It will be copied using Node.js streams.
Create index.js:
const fs = require("fs");
const readStream = fs.createReadStream(
"source.txt"
);
const writeStream = fs.createWriteStream(
"copy.txt"
);
readStream.pipe(writeStream);
writeStream.on("finish", () => {
console.log("File copied successfully.");
});
readStream.on("error", (error) => {
console.log("Read error:", error.message);
});
writeStream.on("error", (error) => {
console.log("Write error:", error.message);
});
Output
File copied successfully.
A new file named copy.txt will contain the same data as source.txt.
Step-by-Step Explanation
First, create a readable stream:
const readStream = fs.createReadStream(
"source.txt"
);
Then create a writable stream:
const writeStream = fs.createWriteStream(
"copy.txt"
);
Connect them:
readStream.pipe(writeStream);
The data automatically flows from:
source.txt
↓
Readable Stream
↓
pipe()
↓
Writable Stream
↓
copy.txt
Question 6: How do you create a Transform Stream?
Problem
Create a Transform stream that converts incoming text to uppercase.
Solution
const {
Readable,
Transform
} = require("stream");
const upperCaseTransform = new Transform({
transform(chunk, encoding, callback) {
const text = chunk
.toString()
.toUpperCase();
callback(null, text);
}
});
const readableStream = Readable.from([
"hello ",
"node.js ",
"streams!"
]);
readableStream
.pipe(upperCaseTransform)
.on("data", (chunk) => {
console.log(chunk.toString());
});
Output
HELLO
NODE.JS
STREAMS!
Step-by-Step Explanation
A Transform stream is a special type of stream that can:
Read data
↓
Change/process data
↓
Output changed data
We create a Transform stream:
const upperCaseTransform = new Transform({
The transform() method receives each chunk:
transform(chunk, encoding, callback) {
We convert the chunk to uppercase:
const text = chunk
.toString()
.toUpperCase();
Then send the transformed data forward:
callback(null, text);
Question 7: What is a Duplex Stream?
Problem
Create a simple Duplex stream that can both receive and provide data.
Solution
const { Duplex } = require("stream");
const duplexStream = new Duplex({
read(size) {
this.push("Data from readable side.");
this.push(null);
},
write(chunk, encoding, callback) {
console.log(
"Received:",
chunk.toString()
);
callback();
}
});
duplexStream.on("data", (chunk) => {
console.log(
"Read:",
chunk.toString()
);
});
duplexStream.write("Hello from writable side.");
duplexStream.end();
Output
Read: Data from readable side.
Received: Hello from writable side.
Step-by-Step Explanation
A Duplex stream has both:
Readable side
Writable side
The read() method controls data coming out of the stream:
read(size) {
this.push("Data from readable side.");
this.push(null);
}
The write() method handles data coming into the stream:
write(chunk, encoding, callback) {
console.log(chunk.toString());
callback();
}
Simple Example
Think of a telephone:
You speak → writable side
You listen ← readable side
A Duplex stream can receive data and provide data independently.
Question 8: How do you monitor stream events?
Problem
Create a readable stream and use important stream events such as data, end, and error.
Solution
const fs = require("fs");
const readStream = fs.createReadStream(
"message.txt",
"utf8"
);
readStream.on("data", (chunk) => {
console.log("DATA EVENT");
console.log(chunk);
});
readStream.on("end", () => {
console.log("END EVENT");
});
readStream.on("error", (error) => {
console.log("ERROR EVENT");
console.log(error.message);
});
Output
For a valid file, you will see something similar to:
DATA EVENT
Welcome to Node.js...
END EVENT
Step-by-Step Explanation
data
The data event is emitted when a chunk of data is available:
readStream.on("data", (chunk) => {
console.log(chunk);
});
end
The end event occurs when there is no more data:
readStream.on("end", () => {
console.log("END EVENT");
});
error
The error event handles stream errors:
readStream.on("error", (error) => {
console.log(error.message);
});
Question 9: How do you control the flow of a readable stream?
Problem
Read a file and temporarily pause the stream after receiving a chunk, then continue it after one second.
Solution
const fs = require("fs");
const readStream = fs.createReadStream(
"message.txt",
"utf8"
);
readStream.on("data", (chunk) => {
console.log("Received data:");
console.log(chunk);
readStream.pause();
console.log("Stream paused.");
setTimeout(() => {
console.log("Stream resumed.");
readStream.resume();
}, 1000);
});
readStream.on("end", () => {
console.log("Reading completed.");
});
Output
The exact number of chunks depends on the file size and stream buffering. You will see output similar to:
Received data:
...
Stream paused.
Stream resumed.
...
Reading completed.
Step-by-Step Explanation
The stream is normally flowing when we listen for data.
To pause it:
readStream.pause();
After one second:
readStream.resume();
starts the flow again.
Question 10: How do you build a practical file-processing pipeline using Streams?
Problem
Create a Node.js program that:
- Reads a text file.
- Converts all text to uppercase.
- Writes the transformed content to another file.
- Uses streams.
- Uses a Transform stream.
- Uses
pipe().
Solution
Create input.txt:
welcome to node.js
streams are useful for large data
learning streams is important
Create index.js:
const fs = require("fs");
const {
Transform
} = require("stream");
const readStream = fs.createReadStream(
"input.txt",
"utf8"
);
const writeStream = fs.createWriteStream(
"output.txt"
);
const upperCaseStream = new Transform({
transform(chunk, encoding, callback) {
const text = chunk
.toString()
.toUpperCase();
callback(null, text);
}
});
readStream
.pipe(upperCaseStream)
.pipe(writeStream);
writeStream.on("finish", () => {
console.log(
"File processed successfully."
);
});
readStream.on("error", (error) => {
console.log(
"Read error:",
error.message
);
});
upperCaseStream.on("error", (error) => {
console.log(
"Transform error:",
error.message
);
});
writeStream.on("error", (error) => {
console.log(
"Write error:",
error.message
);
});
Output
Console:
File processed successfully.
The generated output.txt contains:
WELCOME TO NODE.JS
STREAMS ARE USEFUL FOR LARGE DATA
LEARNING STREAMS IS IMPORTANT
Step-by-Step Explanation
Step 1: Create the readable stream
const readStream = fs.createReadStream(
"input.txt",
"utf8"
);
This reads the input file in chunks.
Step 2: Create the writable stream
const writeStream = fs.createWriteStream(
"output.txt"
);
This writes data to the output file.
Step 3: Create the Transform stream
const upperCaseStream = new Transform({
The Transform stream changes the incoming text.
Step 4: Convert the chunk
const text = chunk
.toString()
.toUpperCase();
Step 5: Pass the transformed data forward
callback(null, text);
Step 6: Connect the streams
readStream
.pipe(upperCaseStream)
.pipe(writeStream);
This creates a complete pipeline:
input.txt
↓
Readable Stream
↓
Transform Stream
↓
Uppercase
↓
Writable Stream
↓
output.txt
Step 7: Detect completion
writeStream.on("finish", () => {
console.log("File processed successfully.");
});
Key Takeaways
- Streams allow Node.js to process data progressively instead of treating an entire data source as one large value.
- A readable stream is used to read or provide data.
- A writable stream is used to receive or write data.
- A Duplex stream supports both reading and writing.
- A Transform stream can modify data as it passes through.
fs.createReadStream()creates a readable file stream.fs.createWriteStream()creates a writable file stream.pipe()connects a readable stream to a writable stream or another compatible stream.- The
dataevent is used when chunks of readable data become available. - The
endevent indicates that a readable stream has no more data to provide. - The
finishevent indicates that a writable stream has finished processing all written data. - The
errorevent can be used to handle stream errors. pause()temporarily stops a readable stream from flowing.resume()continues a paused readable stream.- Streams are particularly useful for large files and continuous data.
- Transform streams are useful for processing data between a source and destination.
- Stream pipelines can connect multiple processing stages together.
- Streams help reduce the need to load an entire large data source into memory at once.
- Streams are widely used with files, HTTP requests, responses, uploads, and downloads.
- Understanding streams is important for becoming comfortable with advanced Node.js programming.
FAQs
1. What are Streams in Node.js?
Streams are objects that allow Node.js to process data progressively, often in smaller pieces called chunks.
Instead of waiting for all data to become available, an application can start processing data as it arrives.
Common stream types are:
Readable
Writable
Duplex
Transform
2. What is a Readable Stream?
A Readable stream provides data that an application can consume.
For example:
const fs = require("fs");
const stream = fs.createReadStream(
"large-file.txt"
);
stream.on("data", (chunk) => {
console.log(chunk);
});
A file readable stream provides the contents of the file in chunks.
3. What is a Writable Stream?
A Writable stream receives data that an application wants to write somewhere.
For example:
const fs = require("fs");
const stream = fs.createWriteStream(
"output.txt"
);
stream.write("Hello Node.js!");
stream.end();
The data is written to output.txt.
4. What is pipe() in Node.js Streams?
pipe() connects a readable stream to a writable stream or another compatible destination.
Example:
const fs = require("fs");
const readStream = fs.createReadStream(
"source.txt"
);
const writeStream = fs.createWriteStream(
"copy.txt"
);
readStream.pipe(writeStream);
Data flows automatically from the readable stream to the writable stream.
5. What is a Transform Stream?
A Transform stream is a type of Duplex stream that can modify data as it passes through.
For example, it can:
- Convert text to uppercase
- Compress data
- Encrypt data
- Modify JSON
- Filter information
Example:
const { Transform } = require("stream");
The incoming data can be processed before being sent to the next stream.
6. Why are Streams useful for large files?
Streams allow data to be processed progressively.
Without streams, an application might attempt to load a large file into memory before processing it.
With streams:
Read chunk
↓
Process chunk
↓
Write chunk
↓
Read next chunk
This approach can reduce memory requirements and is useful when working with large files.
7. What is the difference between Duplex and Transform Streams?
Both can be used for reading and writing.
A Duplex stream has independent readable and writable sides.
A Transform stream is designed to process incoming data and produce corresponding output.
A simple way to remember them is:
Duplex:
Input ↔ Output
Transform:
Input → Process → Output
Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.
