Difficulty: Intermediate
Buffers are Node.js's way of handling raw binary data. JavaScript strings are UTF-16 encoded and are not ideal for working with binary data like files, network packets, images, or cryptographic operations. The Buffer class provides a fixed-size chunk of memory allocated outside the V8 heap, giving you direct access to raw bytes. Buffers are one of the global objects in Node.js and do not require an import.
There are several ways to create Buffers. Buffer.from(string, encoding) creates a Buffer from a string with the specified encoding (default 'utf8'). Buffer.from(array) creates a Buffer from an array of byte values. Buffer.alloc(size) creates a zero-filled Buffer of the specified size in bytes, which is the safe way to allocate Buffers. Buffer.allocUnsafe(size) is faster but the memory may contain old data from previous allocations, so you must fill it before reading. In most cases, use Buffer.alloc for new buffers and Buffer.from for converting data.
Converting between Buffers and strings is done with buffer.toString(encoding). Common encodings include 'utf8' (default, for text), 'ascii' (7-bit ASCII), 'base64' (for encoding binary data as text), 'hex' (hexadecimal representation), 'latin1' (ISO 8859-1), and 'utf16le' (little-endian UTF-16). The encoding parameter is important because the same bytes can represent completely different strings in different encodings. Always be explicit about encoding when converting.
Buffer.concat(bufferArray) merges multiple Buffers into a single Buffer. This is commonly used when collecting chunks from a stream. Buffers are fixed-size once created - you cannot resize them. To combine data, you must create a new Buffer with concat. Buffer.byteLength(string, encoding) returns how many bytes a string will occupy in a given encoding, which differs from string.length for multi-byte characters.
Buffers support array-like operations. You can access individual bytes with bracket notation (buf[0]), iterate with for-of, use slice() to create a view of a portion (shares memory), and use copy() to copy bytes between buffers. The Buffer class is a subclass of Uint8Array, so it is compatible with TypedArray methods. In modern code, you may also encounter ArrayBuffer and typed arrays (Uint8Array, Int32Array, Float64Array), which are the standard JavaScript binary data types. Buffers and TypedArrays can be converted between each other.
// Create from string
const buf1 = Buffer.from('Hello, Node.js!');
console.log('Buffer:', buf1);
console.log('Length:', buf1.length, 'bytes');
console.log('UTF-8:', buf1.toString('utf8'));
console.log('Base64:', buf1.toString('base64'));
console.log('Hex:', buf1.toString('hex'));
// Create from array of bytes
const buf2 = Buffer.from([72, 101, 108, 108, 111]); // ASCII codes
console.log('\nFrom bytes:', buf2.toString());
// Allocate zeroed buffer
const buf3 = Buffer.alloc(8);
console.log('Zeroed:', buf3);
buf3.write('Node');
console.log('After write:', buf3.toString().trim());
// Multi-byte characters
const emoji = Buffer.from('Hello 🌍');
console.log('\nString length:', 'Hello 🌍'.length);
console.log('Buffer length:', emoji.length, 'bytes');
console.log('Byte length:', Buffer.byteLength('Hello 🌍', 'utf8'));
Buffer.from creates a buffer from a string or byte array. toString converts back with the specified encoding. Multi-byte characters like emojis take more bytes than their string length suggests (4 bytes for the globe emoji in UTF-8).
// Common pattern: collecting chunks from a stream
const chunks = [];
// Simulating stream chunks
chunks.push(Buffer.from('First chunk. '));
chunks.push(Buffer.from('Second chunk. '));
chunks.push(Buffer.from('Third chunk.'));
// Concatenate all chunks into one buffer
const combined = Buffer.concat(chunks);
console.log('Combined:', combined.toString());
console.log('Total bytes:', combined.length);
// Slicing (creates a view, shares memory)
const slice = combined.slice(0, 12);
console.log('Slice:', slice.toString());
// Copy (does not share memory)
const copy = Buffer.alloc(12);
combined.copy(copy, 0, 0, 12);
console.log('Copy:', copy.toString());
// Compare buffers
const a = Buffer.from('abc');
const b = Buffer.from('abc');
const c = Buffer.from('def');
console.log('\na equals b:', a.equals(b));
console.log('a equals c:', a.equals(c));
console.log('a compare c:', a.compare(c)); // -1 (a < c)
Buffer.concat is the standard way to assemble chunks from streams. slice() creates a view sharing the same memory (modifying one affects the other). copy() creates an independent copy. equals() and compare() enable Buffer comparison.
// Base64 encoding: convert binary data to text-safe string
const original = 'Node.js Buffers are powerful!';
// Encode to base64
const encoded = Buffer.from(original).toString('base64');
console.log('Original:', original);
console.log('Base64:', encoded);
// Decode from base64
const decoded = Buffer.from(encoded, 'base64').toString('utf8');
console.log('Decoded:', decoded);
// Practical: Creating a basic auth header
const username = 'admin';
const password = 'secret123';
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
console.log('\nAuth header:', `Basic ${credentials}`);
// Decode it back
const [user, pass] = Buffer.from(credentials, 'base64')
.toString('utf8')
.split(':');
console.log('Username:', user);
console.log('Password:', pass);
// Hex encoding: useful for hashes and tokens
const hexData = Buffer.from('secret').toString('hex');
console.log('\nHex:', hexData);
console.log('Back:', Buffer.from(hexData, 'hex').toString());
Base64 encoding converts binary data into text-safe characters, commonly used for HTTP Basic auth headers, embedding images in HTML/CSS, and API tokens. Hex encoding is used for hashes, cryptographic keys, and debugging binary data.
Buffer.from, Buffer.alloc, toString, Buffer.concat, Encoding, TypedArrays