Buffers handle binary data in Node.js. Here's how to use them effectively.
Creating Buffers#
1// Allocate buffer with size
2const buf1 = Buffer.alloc(10); // 10 bytes, initialized to 0
3const buf2 = Buffer.allocUnsafe(10); // 10 bytes, not initialized (faster)
4
5// From string
6const buf3 = Buffer.from('Hello World');
7const buf4 = Buffer.from('Hello World', 'utf8');
8const buf5 = Buffer.from('48656c6c6f', 'hex');
9const buf6 = Buffer.from('SGVsbG8=', 'base64');
10
11// From array
12const buf7 = Buffer.from([72, 101, 108, 108, 111]); // 'Hello'
13
14// From another buffer
15const buf8 = Buffer.from(buf3);
16
17// From ArrayBuffer
18const arrayBuffer = new ArrayBuffer(10);
19const buf9 = Buffer.from(arrayBuffer);
20
21// Concatenate buffers
22const combined = Buffer.concat([buf1, buf3]);Reading and Writing#
1const buf = Buffer.alloc(10);
2
3// Write methods
4buf.writeUInt8(255, 0); // Write 8-bit unsigned int at offset 0
5buf.writeUInt16LE(1000, 1); // Write 16-bit little-endian at offset 1
6buf.writeUInt16BE(1000, 3); // Write 16-bit big-endian at offset 3
7buf.writeInt32LE(-100, 5); // Write 32-bit signed little-endian
8
9// Read methods
10const value1 = buf.readUInt8(0); // 255
11const value2 = buf.readUInt16LE(1); // 1000
12const value3 = buf.readUInt16BE(3); // 1000
13const value4 = buf.readInt32LE(5); // -100
14
15// Float and double
16const floatBuf = Buffer.alloc(8);
17floatBuf.writeFloatLE(3.14, 0);
18floatBuf.writeDoubleLE(3.14159265359, 0);
19
20const pi = floatBuf.readFloatLE(0);
21const piPrecise = floatBuf.readDoubleLE(0);
22
23// BigInt (64-bit)
24const bigBuf = Buffer.alloc(8);
25bigBuf.writeBigInt64LE(BigInt('9223372036854775807'), 0);
26const bigValue = bigBuf.readBigInt64LE(0);String Encoding#
1const str = 'Hello, 世界!';
2
3// Encode to buffer
4const utf8Buf = Buffer.from(str, 'utf8');
5const utf16Buf = Buffer.from(str, 'utf16le');
6
7console.log(utf8Buf.length); // 14 bytes
8console.log(utf16Buf.length); // 22 bytes
9
10// Decode to string
11const decoded = utf8Buf.toString('utf8');
12
13// Supported encodings:
14// - utf8, utf-8
15// - utf16le, utf-16le
16// - latin1, binary
17// - base64, base64url
18// - hex
19// - ascii
20
21// Encoding conversions
22const hexString = utf8Buf.toString('hex');
23// '48656c6c6f2c20e4b896e7958c21'
24
25const base64 = utf8Buf.toString('base64');
26// 'SGVsbG8sIOS4lueVjCE='
27
28// Convert between encodings
29function convertEncoding(str, from, to) {
30 return Buffer.from(str, from).toString(to);
31}
32
33convertEncoding('48656c6c6f', 'hex', 'utf8'); // 'Hello'Buffer Manipulation#
1const buf = Buffer.from('Hello World');
2
3// Slice (creates view, not copy)
4const slice = buf.slice(0, 5); // 'Hello'
5slice[0] = 74; // Changes original too!
6
7// Subarray (same as slice)
8const sub = buf.subarray(6, 11); // 'World'
9
10// Copy (creates actual copy)
11const copy = Buffer.alloc(5);
12buf.copy(copy, 0, 0, 5);
13
14// Fill
15const fillBuf = Buffer.alloc(10);
16fillBuf.fill('a'); // 'aaaaaaaaaa'
17fillBuf.fill(0); // All zeros
18
19// Compare
20const a = Buffer.from('abc');
21const b = Buffer.from('abd');
22console.log(a.compare(b)); // -1 (a < b)
23console.log(Buffer.compare(a, b)); // -1
24
25// Equals
26console.log(a.equals(Buffer.from('abc'))); // true
27
28// Index operations
29const indexBuf = Buffer.from('Hello');
30console.log(indexBuf.indexOf('l')); // 2
31console.log(indexBuf.lastIndexOf('l')); // 3
32console.log(indexBuf.includes('ell')); // trueWorking with Files#
1const fs = require('fs');
2
3// Read file as buffer
4const data = fs.readFileSync('image.png');
5console.log(data); // <Buffer 89 50 4e 47 ...>
6
7// Check file type by magic bytes
8function getFileType(buffer) {
9 const signatures = {
10 png: [0x89, 0x50, 0x4e, 0x47],
11 jpg: [0xff, 0xd8, 0xff],
12 gif: [0x47, 0x49, 0x46, 0x38],
13 pdf: [0x25, 0x50, 0x44, 0x46],
14 };
15
16 for (const [type, sig] of Object.entries(signatures)) {
17 if (sig.every((byte, i) => buffer[i] === byte)) {
18 return type;
19 }
20 }
21
22 return 'unknown';
23}
24
25// Write buffer to file
26const imageData = Buffer.from([0x89, 0x50, 0x4e, 0x47, /* ... */]);
27fs.writeFileSync('output.png', imageData);
28
29// Stream processing
30const readStream = fs.createReadStream('large.bin');
31readStream.on('data', (chunk) => {
32 // chunk is a Buffer
33 processChunk(chunk);
34});Binary Protocols#
1// Simple binary protocol
2class BinaryProtocol {
3 static encode(message) {
4 const payload = Buffer.from(JSON.stringify(message));
5 const header = Buffer.alloc(4);
6 header.writeUInt32BE(payload.length, 0);
7 return Buffer.concat([header, payload]);
8 }
9
10 static decode(buffer) {
11 const length = buffer.readUInt32BE(0);
12 const payload = buffer.slice(4, 4 + length);
13 return JSON.parse(payload.toString());
14 }
15}
16
17// Fixed-length record
18class Record {
19 constructor(id, name, score) {
20 this.id = id;
21 this.name = name.padEnd(20, '\0');
22 this.score = score;
23 }
24
25 toBuffer() {
26 const buf = Buffer.alloc(28); // 4 + 20 + 4
27 buf.writeUInt32BE(this.id, 0);
28 buf.write(this.name, 4, 20, 'utf8');
29 buf.writeFloatBE(this.score, 24);
30 return buf;
31 }
32
33 static fromBuffer(buf) {
34 const id = buf.readUInt32BE(0);
35 const name = buf.slice(4, 24).toString('utf8').replace(/\0/g, '');
36 const score = buf.readFloatBE(24);
37 return new Record(id, name, score);
38 }
39}
40
41// Usage
42const record = new Record(1, 'Alice', 95.5);
43const buf = record.toBuffer();
44const restored = Record.fromBuffer(buf);Crypto Operations#
1const crypto = require('crypto');
2
3// Hash a buffer
4function hashBuffer(buffer, algorithm = 'sha256') {
5 return crypto.createHash(algorithm).update(buffer).digest();
6}
7
8const data = Buffer.from('Hello World');
9const hash = hashBuffer(data);
10console.log(hash.toString('hex'));
11
12// Generate random bytes
13const randomBytes = crypto.randomBytes(32);
14
15// Encrypt/decrypt with buffer
16function encrypt(buffer, key, iv) {
17 const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
18 return Buffer.concat([cipher.update(buffer), cipher.final()]);
19}
20
21function decrypt(buffer, key, iv) {
22 const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
23 return Buffer.concat([decipher.update(buffer), decipher.final()]);
24}
25
26// HMAC
27function hmac(buffer, key) {
28 return crypto.createHmac('sha256', key).update(buffer).digest();
29}ArrayBuffer Interop#
1// Buffer to ArrayBuffer
2const buffer = Buffer.from('Hello');
3const arrayBuffer = buffer.buffer.slice(
4 buffer.byteOffset,
5 buffer.byteOffset + buffer.byteLength
6);
7
8// ArrayBuffer to Buffer
9const ab = new ArrayBuffer(8);
10const buf = Buffer.from(ab);
11
12// TypedArray interop
13const uint8 = new Uint8Array([1, 2, 3, 4]);
14const bufFromTyped = Buffer.from(uint8.buffer);
15
16// Shared memory (careful!)
17const sharedBuf = Buffer.from(uint8.buffer);
18sharedBuf[0] = 99;
19console.log(uint8[0]); // 99 - they share memory!
20
21// Safe copy
22const safeCopy = Buffer.from(uint8);
23safeCopy[0] = 99;
24console.log(uint8[0]); // 1 - original unchangedPerformance Tips#
1// Pre-allocate for known sizes
2const fixedBuf = Buffer.allocUnsafe(1024);
3
4// Reuse buffers
5class BufferPool {
6 constructor(size, count) {
7 this.pool = Array.from({ length: count }, () => Buffer.alloc(size));
8 this.available = [...this.pool];
9 }
10
11 acquire() {
12 return this.available.pop() || Buffer.alloc(this.pool[0].length);
13 }
14
15 release(buffer) {
16 buffer.fill(0);
17 if (this.available.length < this.pool.length) {
18 this.available.push(buffer);
19 }
20 }
21}
22
23// Avoid string concatenation in hot paths
24// BAD:
25let result = '';
26for (const chunk of chunks) {
27 result += chunk.toString();
28}
29
30// GOOD:
31const result = Buffer.concat(chunks).toString();Best Practices#
Memory:
✓ Use Buffer.alloc for sensitive data
✓ Use Buffer.allocUnsafe for performance
✓ Pool buffers when allocating many
✓ Clear sensitive data when done
Encoding:
✓ Always specify encoding
✓ Use Buffer.byteLength for string sizes
✓ Handle multi-byte characters properly
✓ Validate input encodings
Safety:
✓ Check bounds before reading
✓ Use slice/subarray carefully
✓ Copy when sharing externally
✓ Zero sensitive buffers
Performance:
✓ Avoid unnecessary conversions
✓ Use concat over += for binary
✓ Pre-allocate when size is known
✓ Stream large data
Conclusion#
Buffers are essential for binary data in Node.js. Understand encoding conversions, use appropriate read/write methods for data types, and be mindful of memory sharing. Use Buffer.alloc for security, allocUnsafe for performance, and always validate bounds when working with binary protocols.