Aplicações Node.js trabalham com arquivos, redes, APIs, bancos e protocolos que transportam bytes, não strings abstratas. O TextEncoder e TextDecoder no Node.js fazem a ponte entre texto JavaScript e dados binários usando interfaces padronizadas da Web.
TextEncoder converte strings para bytes UTF-8. TextDecoder converte ArrayBuffer, Buffer ou TypedArray para texto, com suporte a codificações, streaming e tratamento de sequências inválidas. Essas APIs são úteis em fetch, Web Streams, criptografia, sockets, arquivos e Worker Threads.
Neste guia, você aprenderá a codificar e decodificar, lidar com Unicode, chunks divididos, BOM, fatal errors, encodeInto, Buffer, streams, limites e testes.
O que são TextEncoder e TextDecoder?
São APIs padronizadas para conversão entre texto e bytes. A documentação oficial de TextEncoder no Node.js mostra métodos e propriedades. A documentação de TextDecoder na MDN explica decodificação, streaming e opções.
Para dados binários, consulte Buffer no Node.js. Para fluxos de texto, veja Web Streams API no Node.js. O artigo String Decoder no Node.js apresenta a API tradicional para streams.
TextEncoder básico
const encoder = new TextEncoder();
const bytes = encoder.encode('Olá, Node.js!');
console.log(bytes);O resultado é um Uint8Array em UTF-8. TextEncoder padronizado usa UTF-8.
Tamanho em bytes
const text = 'ação';
const bytes = encoder.encode(text);
console.log(text.length);
console.log(bytes.byteLength);O comprimento da string mede unidades UTF-16, enquanto byteLength mede bytes UTF-8. Caracteres acentuados e emojis ocupam vários bytes.
TextDecoder básico
const decoder = new TextDecoder('utf-8');
const text = decoder.decode(bytes);Se nenhuma codificação for informada, UTF-8 é usada na maioria dos casos.
Decodificando Buffer
const buffer = Buffer.from('Olá', 'utf8');
const text = decoder.decode(buffer);Buffer é uma subclasse de Uint8Array e pode ser passado diretamente.
encodeInto()
const destination = new Uint8Array(1024);
const result = encoder.encodeInto(
'mensagem importante',
destination
);
console.log(result.read);
console.log(result.written);encodeInto() escreve em um buffer fornecido, reduzindo alocações em loops de alta frequência.
Buffer pequeno
Quando o destino não cabe, apenas parte da string é consumida:
const destination = new Uint8Array(5);
const result = encoder.encodeInto('abcdef', destination);Use read e written para continuar sem cortar pares substitutos incorretamente.
Unicode e surrogate pairs
JavaScript representa strings em UTF-16. Emojis e alguns símbolos usam duas unidades de código. TextEncoder converte para a sequência UTF-8 apropriada.
const bytes = encoder.encode('🚀');Não divida strings arbitrariamente por índice quando precisa preservar caracteres completos.
Sequências inválidas
TextDecoder pode substituir bytes inválidos pelo caractere de substituição:
const invalid = new Uint8Array([0xff, 0xfe]);
const text = new TextDecoder().decode(invalid);Modo fatal
const decoder = new TextDecoder('utf-8', {
fatal: true
});
try {
decoder.decode(invalid);
} catch (error) {
console.error('UTF-8 inválido');
}Use fatal quando dados corrompidos precisam gerar erro em vez de substituição silenciosa.
BOM
Byte Order Mark pode aparecer no início de arquivos. TextDecoder possui opção relacionada a BOM:
const decoder = new TextDecoder('utf-8', {
ignoreBOM: false
});Teste conforme o formato. Um BOM inesperado pode alterar chaves ou o primeiro valor analisado.
Decodificação em chunks
Um caractere UTF-8 pode ser dividido entre chunks. Use stream: true:
const decoder = new TextDecoder();
let output = '';
for (const chunk of chunks) {
output += decoder.decode(chunk, {
stream: true
});
}
output += decoder.decode();A chamada final sem bytes libera qualquer estado restante.
Erro ao decodificar cada chunk isoladamente
for (const chunk of chunks) {
output += new TextDecoder().decode(chunk);
}Esse padrão pode inserir caracteres de substituição quando uma sequência multibyte está dividida.
TextDecoderStream
const textStream = byteStream.pipeThrough(
new TextDecoderStream('utf-8', {
fatal: true
})
);TextDecoderStream mantém estado entre chunks e integra com Web Streams.
TextEncoderStream
const byteStream = textStream.pipeThrough(
new TextEncoderStream()
);É útil para transformar strings em bytes antes de enviar para fetch ou arquivo.
Arquivo em memória
const fs = require('node:fs/promises');
const bytes = await fs.readFile('./message.txt');
const text = new TextDecoder('utf-8', {
fatal: true
}).decode(bytes);readFile() carrega todo o arquivo. Para arquivos grandes, use stream.
Arquivo em stream
Converta o stream tradicional ou use StringDecoder. O importante é manter estado entre chunks e aplicar limite de tamanho.
Veja File System no Node.js.
Fetch
const response = await fetch(url);
for await (const chunk of response.body) {
// chunks binários
}Para texto grande, use TextDecoderStream em vez de response.text(), que acumula tudo.
Consulte Fetch Nativo no Node.js.
Protocolos de linha
Depois de decodificar corretamente, acumule apenas até encontrar delimitador:
let pending = '';
for await (const chunk of textStream) {
pending += chunk;
let index;
while ((index = pending.indexOf('\n')) !== -1) {
const line = pending.slice(0, index);
pending = pending.slice(index + 1);
processLine(line);
}
if (pending.length > MAX_LINE_LENGTH) {
throw new Error('Linha muito longa');
}
}JSON em stream
JSON comum exige documento completo. Para fluxos grandes, use NDJSON ou parser incremental com limites.
Criptografia
const data = new TextEncoder().encode(message);
const digest = await crypto.subtle.digest(
'SHA-256',
data
);Use a mesma codificação em todos os sistemas. Veja Web Crypto API no Node.js.
Assinaturas
Uma diferença de normalização Unicode ou final de linha altera bytes e assinatura. Protocolos precisam definir a representação exata.
Normalização Unicode
const normalized = input.normalize('NFC');
const bytes = encoder.encode(normalized);Não normalize automaticamente se o protocolo exige bytes originais.
Outras codificações
TextDecoder pode aceitar codificações além de UTF-8 conforme suporte:
const decoder = new TextDecoder('windows-1252');TextEncoder continua produzindo UTF-8. Para gerar outra codificação, use biblioteca ou API específica.
Codificação desconhecida
Dados sem charset não podem ser decodificados com certeza. Use metadados confiáveis, heurística limitada ou rejeite entrada ambígua.
Content-Type
Um header pode declarar charset:
text/plain; charset=utf-8Não confie cegamente no header de origem desconhecida. Aplique limites e modo fatal quando necessário.
Performance
Reutilize instâncias de TextEncoder e TextDecoder quando apropriado. Em decodificação streaming, uma instância representa o estado de um fluxo e não deve ser compartilhada entre fluxos concorrentes.
Concorrência
Crie um decoder por arquivo, socket ou resposta. Misturar chunks de origens diferentes corrompe o estado.
Memória
Concatenar strings repetidamente pode criar cópias. Processe linhas ou registros e descarte conteúdo já consumido.
Limites
- tamanho total de texto;
- tamanho por linha;
- quantidade de registros;
- tempo de leitura;
- profundidade de parsing posterior.
Worker Threads
TypedArrays podem ser transferidos para workers para processamento pesado. Veja Worker Threads no Node.js. Não transfira um ArrayBuffer que ainda será usado na thread original.
Testes
Cubra:
- ASCII;
- acentos;
- emoji;
- sequência dividida;
- UTF-8 inválido;
- BOM;
- arquivo vazio;
- linha longa;
- encodeInto parcial;
- charset alternativo.
Erros comuns
- Assumir um byte por caractere: tamanhos ficam incorretos.
- Decodificar chunks isolados: Unicode é corrompido.
- Ignorar bytes inválidos: dados inconsistentes passam.
- Compartilhar decoder entre fluxos: estados se misturam.
- Carregar arquivo gigante: memória cresce.
- Assinar strings sem codificação definida: sistemas divergem.
- Não finalizar decode: bytes pendentes não são processados.
Boas práticas
- Defina UTF-8 explicitamente.
- Use stream para chunks.
- Crie um decoder por fluxo.
- Use fatal em formatos estritos.
- Finalize a decodificação.
- Limite linhas e total.
- Reutilize encoder.
- Use encodeInto em caminhos críticos.
- Teste Unicode dividido.
- Documente normalização.
Conclusão
O TextEncoder e TextDecoder no Node.js oferecem conversão padronizada entre strings e bytes, com integração a fetch, Web Streams, criptografia e Worker Threads.
O ponto crítico é lembrar que texto e bytes não têm correspondência simples. Unicode pode ocupar vários bytes e sequências podem atravessar chunks. Com streaming, modo fatal, limites e uma codificação definida, aplicações evitam corrupção silenciosa e processam texto binário de forma previsível.




