TypeScript

What is the difference between a Node.js stream and a Node.js buffer?

Think of a buffer as a bucket. You fill it up, then you use the water. Think of a stream as a pipe. Water flows through it continuously.

23 Apr 2024

What is the difference between a Node.js stream and a Node.js buffer?

Think of a buffer as a bucket. You fill it up, then you use the water. Think of a stream as a pipe. Water flows through it continuously.

That's the core difference in Node.js.

Buffer — load everything into memory

A buffer is a chunk of binary data sitting in memory. You read the whole thing at once, work with it, then move on.

Text
import * as fs from 'fs';

const buffer = Buffer.from('Hello, World!', 'utf8');
fs.writeFileSync('example.txt', buffer.toString());

const fileContent = fs.readFileSync('example.txt', 'utf8');
console.log(fileContent);

Buffers work great for small files and quick operations. But load a 2GB video file into a buffer and your process crashes with an out-of-memory error.

Stream — process data in chunks

A stream processes data piece by piece as it arrives. You never hold the entire dataset in memory.

Text
import * as fs from 'fs';

const stream = fs.createReadStream('largefile.txt', 'utf8');
stream.on('data', (chunk) => {
  console.log(chunk.toString());
});

stream.pipe(process.stdout);

Streams shine for large files, network I/O, and real-time processing like video or audio.

When to use which

Buffers: Small data, file reads where you need the entire content at once, encryption operations, quick transformations.

Streams: Large files, real-time data, piping data between sources, anything where memory efficiency matters.

The trade-off

Buffers are simpler to reason about — you have all the data, do what you want. Streams are more complex (backpressure, error handling across chunks) but they scale. If your data fits comfortably in memory, a buffer is fine. If it doesn't, you need a stream.

Keep reading