Buffer
The Buffer class is a global type in Node.js for working with binary data directly. It represents a fixed-length sequence of bytes. Many Node.js APIs use Buffers to handle file I/O, network communications, and other operations that involve raw binary data.
In early Node.js versions, Buffers were the primary way to work with binary data because JavaScript had no native support for typed arrays. While TypedArrays now exist in the ECMAScript standard, Buffer remains essential in Node.js for performance-critical operations and interoperability with file systems, networks, and native modules.
Creating Buffers
Buffer provides several static methods for creating new buffers:
Buffer.alloc()
Allocates a zero-filled buffer of a specific size:
const buf = Buffer.alloc(10);
console.log(buf); // <Buffer 00 00 00 00 00 00 00 00 00 00>
You can pre-fill the buffer with a specific value and encoding:
const buf = Buffer.alloc(10, 'a');
console.log(buf.toString()); // "aaaaaaaaaa"
const utf8Buf = Buffer.alloc(10, '\u00ff', 'utf8');
Buffer.allocUnsafe()
Creates an uninitialized buffer. This is faster than Buffer.alloc() because it skips zero-filling, but the buffer may contain old data from memory. Always overwrite the contents before use:
const buf = Buffer.allocUnsafe(10);
console.log(buf); // May contain random data from memory
buf.fill(0); // Always initialize before use
Buffer.from()
Creates a buffer from an existing source:
const buf1 = Buffer.from('hello');
const buf2 = Buffer.from([1, 2, 3, 4]);
const buf3 = Buffer.from(Buffer.from('world'));
Reading and Writing
Writing strings
Use buf.write() to write a string at a specific offset:
const buf = Buffer.alloc(10);
buf.write('hello');
console.log(buf.toString()); // "hello"
buf.write('world', 5); // Write at offset 5
console.log(buf.toString()); // "helloworld"
Reading as string
Use buf.toString() to convert buffer contents back to a string:
const buf = Buffer.from('hello world');
console.log(buf.toString()); // "hello world"
console.log(buf.toString('utf8', 0, 5)); // "hello"
Reading and writing numeric values
Buffer provides methods for reading and writing integers and floats at specific byte offsets:
const buf = Buffer.alloc(8);
// Write a 32-bit integer at offset 0
buf.writeInt32BE(12345, 0);
console.log(buf.readInt32BE(0)); // 12345
// Write a 16-bit unsigned integer
buf.writeUInt16LE(256, 4);
console.log(buf.readUInt16LE(4)); // 256
// Little-endian vs big-endian
buf.writeUInt32LE(0x12345678, 0);
console.log(buf.readUInt32LE(0).toString(16)); // "12345678"
The LE suffix means little-endian byte order. Use BE for big-endian.
Buffer Operations
concat()
Merges multiple buffers into a single buffer:
const buf1 = Buffer.from('hello');
const buf2 = Buffer.from(' ');
const buf3 = Buffer.from('world');
const combined = Buffer.concat([buf1, buf2, buf3]);
console.log(combined.toString()); // "hello world"
copy()
Copies data from one buffer to another:
const source = Buffer.from('hello world');
const target = Buffer.alloc(20);
source.copy(target);
console.log(target.toString()); // "hello world"
source.copy(target, 0, 6, 11); // Copy from offset 6 to 11
console.log(target.toString()); // "world world"
slice()
Returns a new buffer referencing a portion of the original. The returned buffer shares memory with the original:
const buf = Buffer.from('hello world');
const slice = buf.slice(0, 5);
console.log(slice.toString()); // "hello"
slice[0] = 97; // Changes both buffers (shared memory)
console.log(buf.toString()); // "aello world"
fill()
Fills the buffer with a specified value:
const buf = Buffer.alloc(10);
buf.fill('a');
console.log(buf.toString()); // "aaaaaaaaaa"
buf.fill('b', 2, 5); // Fill from index 2 to 5
console.log(buf.toString()); // "aabbbaaaaa"
Buffers and TypedArrays
Buffer is a subclass of Uint8Array. This means you can use TypedArray methods on buffers and pass buffers wherever TypedArrays are expected:
const buf = Buffer.from([1, 2, 3, 4, 5]);
// Use TypedArray methods
const subarray = buf.subarray(1, 3);
console.log(subarray.toString()); // "2,3"
console.log(buf.includes(3)); // true
console.log(buf.indexOf(3)); // 2
Buffer also works the other way—pass a TypedArray to Buffer.from() to copy its contents:
const typedArray = new Uint8Array([1, 2, 3, 4, 5]);
const buf = Buffer.from(typedArray);
console.log(buf.toString()); // "\x01\x02\x03\x04\x05"
The underlying ArrayBuffer can be shared between Buffer and TypedArrays for zero-copy operations:
const buf = Buffer.alloc(10);
const typedArray = new Uint8Array(buf.buffer, buf.byteOffset, buf.length);
Character Encodings
When converting between buffers and strings, you can specify a character encoding:
| Encoding | Description |
|---|---|
utf8 | Default. Multi-byte Unicode (default) |
utf16le | Little-endian UTF-16 |
latin1 | ISO-8859-1, single byte per character |
base64 | Base64-encoded string |
hex | Hexadecimal, two chars per byte |
const str = 'Hello, 世界';
const utf8 = Buffer.from(str, 'utf8');
console.log(utf8.toString('hex')); // "48656c6c6f2c e4b896e7958c"
console.log(utf8.toString('base64')); // "SGVsbG8g5Li55aG9"
console.log(utf8.toString('latin1')); // "Hello, ????"
Security Note
Buffer.allocUnsafe() and Buffer.allocUnsafeSlow() allocate memory without initializing it. The memory may contain sensitive data from previous operations, including passwords, encryption keys, or other private information.
Never use these methods when:
- Creating buffers that might be exposed to other code
- Handling sensitive data without immediately filling the buffer
For most cases, use Buffer.alloc() instead. It is slightly slower but guarantees the buffer is zero-filled.
If you must use Buffer.allocUnsafe() for performance reasons, always fill the buffer completely before exposing it:
const buf = Buffer.allocUnsafe(1024);
// ... write your data to the buffer ...
// Do NOT expose buf until you have written to every byte