Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nodejs/node/llms.txt
Use this file to discover all available pages before exploring further.
The node:string_decoder module provides an API for decoding Buffer objects into strings while preserving encoded multi-byte UTF-8 and UTF-16 characters.
Installation
import { StringDecoder } from 'node:string_decoder';
// or
const { StringDecoder } = require('node:string_decoder');
Why Use StringDecoder?
When converting buffers to strings, multi-byte characters can be split across buffer boundaries. StringDecoder handles this gracefully by buffering incomplete characters.
Problem without StringDecoder:
import { Buffer } from 'node:buffer';
// Euro symbol (€) is 3 bytes: 0xE2 0x82 0xAC
const euro = Buffer.from([0xE2, 0x82, 0xAC]);
// Splitting the character incorrectly
const part1 = Buffer.from([0xE2]);
const part2 = Buffer.from([0x82, 0xAC]);
console.log(part1.toString()); // � (replacement character)
console.log(part2.toString()); // �� (garbage)
Solution with StringDecoder:
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
const decoder = new StringDecoder('utf8');
const part1 = Buffer.from([0xE2]);
const part2 = Buffer.from([0x82, 0xAC]);
console.log(decoder.write(part1)); // '' (buffered)
console.log(decoder.write(part2)); // '€' (complete)
Class: StringDecoder
new StringDecoder([encoding])
Creates a new StringDecoder instance.
Parameters:
encoding - Character encoding (default: 'utf8')
Supported encodings:
'utf8' - UTF-8 encoding (default)
'utf16le' (or 'ucs2') - UTF-16 Little Endian
'base64' - Base64 encoding
'base64url' - Base64 URL-safe encoding
'hex' - Hexadecimal encoding
'ascii' - ASCII encoding
'binary' (or 'latin1') - Latin-1 encoding
Example:
import { StringDecoder } from 'node:string_decoder';
const decoder = new StringDecoder('utf8');
stringDecoder.write(buffer)
Returns a decoded string, buffering incomplete multi-byte characters.
Parameters:
Returns: - Decoded string
Example:
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
const decoder = new StringDecoder('utf8');
// Cent symbol (¢) is 2 bytes: 0xC2 0xA2
const cent = Buffer.from([0xC2, 0xA2]);
console.log(decoder.write(cent)); // '¢'
// Euro symbol (€) is 3 bytes: 0xE2 0x82 0xAC
const euro = Buffer.from([0xE2, 0x82, 0xAC]);
console.log(decoder.write(euro)); // '€'
stringDecoder.end([buffer])
Returns any remaining buffered input as a string and resets the decoder.
Parameters:
buffer - Optional final bytes to decode
Returns: - Remaining decoded string
Example:
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
const decoder = new StringDecoder('utf8');
// Euro symbol split across writes
decoder.write(Buffer.from([0xE2]));
decoder.write(Buffer.from([0x82]));
const result = decoder.end(Buffer.from([0xAC]));
console.log(result); // '€'
// Decoder is now reset and can be reused
const newResult = decoder.write(Buffer.from('hello'));
console.log(newResult); // 'hello'
Common Use Cases
Streaming Text Data
import { StringDecoder } from 'node:string_decoder';
import { createReadStream } from 'node:fs';
const decoder = new StringDecoder('utf8');
const stream = createReadStream('file.txt');
stream.on('data', (chunk) => {
// Properly handles multi-byte characters across chunks
const text = decoder.write(chunk);
console.log(text);
});
stream.on('end', () => {
// Output any remaining buffered data
const remaining = decoder.end();
if (remaining) {
console.log(remaining);
}
});
Processing Network Data
import { StringDecoder } from 'node:string_decoder';
import { createServer } from 'node:net';
const server = createServer((socket) => {
const decoder = new StringDecoder('utf8');
socket.on('data', (chunk) => {
const text = decoder.write(chunk);
console.log('Received:', text);
});
socket.on('end', () => {
const remaining = decoder.end();
if (remaining) {
console.log('Final:', remaining);
}
});
});
server.listen(8000);
Parsing Chunked Responses
import { StringDecoder } from 'node:string_decoder';
import https from 'node:https';
https.get('https://api.example.com/data', (res) => {
const decoder = new StringDecoder('utf8');
let data = '';
res.on('data', (chunk) => {
data += decoder.write(chunk);
});
res.on('end', () => {
data += decoder.end();
console.log(JSON.parse(data));
});
});
Handling Different Encodings
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
// UTF-8 (default)
const utf8Decoder = new StringDecoder('utf8');
console.log(utf8Decoder.write(Buffer.from('Hello')));
// 'Hello'
// UTF-16 Little Endian
const utf16Decoder = new StringDecoder('utf16le');
console.log(utf16Decoder.write(Buffer.from('Hello', 'utf16le')));
// 'Hello'
// Base64
const base64Decoder = new StringDecoder('base64');
console.log(base64Decoder.write(Buffer.from('SGVsbG8=')));
// 'Hello' (base64 decoded)
// Hexadecimal
const hexDecoder = new StringDecoder('hex');
console.log(hexDecoder.write(Buffer.from('48656c6c6f', 'hex')));
// 'Hello'
Multi-byte Character Examples
UTF-8 Characters
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
const decoder = new StringDecoder('utf8');
// 1-byte character (ASCII)
const a = Buffer.from([0x41]);
console.log(decoder.write(a)); // 'A'
// 2-byte character (¢ - cent)
const cent = Buffer.from([0xC2, 0xA2]);
console.log(decoder.write(cent)); // '¢'
// 3-byte character (€ - euro)
const euro = Buffer.from([0xE2, 0x82, 0xAC]);
console.log(decoder.write(euro)); // '€'
// 4-byte character (𝌆 - musical symbol)
const musical = Buffer.from([0xF0, 0x9D, 0x8C, 0x86]);
console.log(decoder.write(musical)); // '𝌆'
Incomplete Character Handling
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
const decoder = new StringDecoder('utf8');
// Euro symbol (€) split across three writes
const byte1 = Buffer.from([0xE2]);
const byte2 = Buffer.from([0x82]);
const byte3 = Buffer.from([0xAC]);
console.log(decoder.write(byte1)); // '' (buffered, incomplete)
console.log(decoder.write(byte2)); // '' (buffered, still incomplete)
console.log(decoder.write(byte3)); // '€' (complete!)
Mixed Content
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
const decoder = new StringDecoder('utf8');
// ASCII followed by incomplete multi-byte character
const mixed = Buffer.from([0x48, 0x69, 0xE2]); // 'Hi' + incomplete €
console.log(decoder.write(mixed)); // 'Hi' (€ buffered)
// Complete the multi-byte character
const completion = Buffer.from([0x82, 0xAC]);
console.log(decoder.write(completion)); // '€'
StringDecoder vs buffer.toString()
When to Use StringDecoder
// ✅ Use StringDecoder for streaming/chunked data
import { StringDecoder } from 'node:string_decoder';
const decoder = new StringDecoder('utf8');
stream.on('data', (chunk) => {
const text = decoder.write(chunk); // Handles split characters
});
When to Use buffer.toString()
// ✅ Use toString() for complete buffers
import { Buffer } from 'node:buffer';
const completeBuffer = Buffer.from('Complete message', 'utf8');
const text = completeBuffer.toString(); // Simple and efficient
Best Practices
- Use for streaming data - Essential when processing chunked data
- Call end() when done - Always call
end() to flush buffered data
- Choose correct encoding - Match the encoding of your source data
- Reuse instances - Create one decoder per stream, not per chunk
- Handle errors - Validate encoding parameters
import { StringDecoder } from 'node:string_decoder';
import { Buffer } from 'node:buffer';
// ✅ Good: Reuse decoder instance
const decoder = new StringDecoder('utf8');
for (const chunk of chunks) {
const text = decoder.write(chunk);
}
const final = decoder.end();
// ❌ Bad: Creating decoder per chunk
for (const chunk of chunks) {
const decoder = new StringDecoder('utf8');
const text = decoder.write(chunk);
}
- Buffer - Binary data handling
- util - Utility functions including
TextDecoder
- stream - Stream processing