Handling Streams
1. Understanding Node.js Streams
| Type | Use |
|---|---|
| Readable | Source (file, HTTP req) |
| Writable | Sink (file, HTTP res) |
| Duplex | Both (TCP socket) |
| Transform | Modify chunks (gzip, hash) |
2. Piping Streams to Response
Example: Pipe a file
import { createReadStream } from "node:fs";
app.get("/files/report.csv", (req, res) => {
res.type("text/csv").attachment("report.csv");
createReadStream("./reports/report.csv").pipe(res);
});
3. Reading Large Files
Example: Highwatermark
createReadStream("./big.bin", { highWaterMark: 1024 * 1024 }).pipe(res); // 1 MB chunks
4. Sending Streaming Responses
Example: Stream JSON array
app.get("/users.ndjson", async (req, res) => {
res.type("application/x-ndjson");
for await (const user of db.users.cursor()) {
if (!res.write(JSON.stringify(user) + "\n")) {
await new Promise(r => res.once("drain", r));
}
}
res.end();
});
5. Handling Stream Errors
Example: pipeline()
import { pipeline } from "node:stream/promises";
app.get("/download", async (req, res, next) => {
try {
await pipeline(createReadStream("./big.bin"), res);
} catch (err) { next(err); }
});
6. Using Transform Streams
Example: Inline gzip
import { createGzip } from "node:zlib";
app.get("/big.json.gz", (req, res) => {
res.set({ "Content-Type": "application/json", "Content-Encoding": "gzip" });
pipeline(createReadStream("./big.json"), createGzip(), res).catch(err => res.destroy(err));
});
7. Implementing Backpressure
Example: Honor write() return
function emit(res, chunk) {
if (!res.write(chunk)) {
return new Promise(r => res.once("drain", r));
}
}
Note:
pipeline() handles backpressure automatically — prefer it over manual pipe() chains.8. Streaming File Uploads
Example: Stream upload to S3
import busboy from "busboy";
import { Upload } from "@aws-sdk/lib-storage";
app.post("/upload", (req, res, next) => {
const bb = busboy({ headers: req.headers, limits: { fileSize: 100 * 1024 * 1024 } });
bb.on("file", (name, file, info) => {
const upload = new Upload({
client: s3,
params: { Bucket: "uploads", Key: info.filename, Body: file, ContentType: info.mimeType }
});
upload.done().then(() => res.json({ ok: true })).catch(next);
});
req.pipe(bb);
});
9. Using stream.pipeline() Method
Example: Multi-stage pipeline
import { pipeline } from "node:stream/promises";
import { createReadStream, createWriteStream } from "node:fs";
import { createBrotliCompress } from "node:zlib";
await pipeline(createReadStream("in.txt"), createBrotliCompress(), createWriteStream("out.txt.br"));
10. Implementing Server-Sent Events (SSE)
Example: SSE basics
app.get("/events", (req, res) => {
res.set({
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive"
});
res.flushHeaders();
const tick = setInterval(() => res.write(`data: ${Date.now()}\n\n`), 1000);
req.on("close", () => clearInterval(tick));
});