MessagePack no Node.js serializa estruturas semelhantes a JSON em formato binário compacto. Ele preserva objetos, arrays, strings, números, booleanos, binários e null, sendo útil em caches, WebSocket, RPC e mensagens internas quando tamanho e velocidade importam.
Diferente de Avro e Protobuf, MessagePack não exige schema por padrão. Essa flexibilidade facilita adoção, mas transfere para a aplicação a responsabilidade de validar estrutura, compatibilidade e limites. Um payload decodificado ainda é entrada não confiável.
Instalação e primeiro exemplo
import { encode, decode } from '@msgpack/msgpack';
const payload = {
id: 'evt-123',
type: 'order.created.v1',
amountCents: 12990,
tags: ['nodejs', 'api']
};
const bytes = encode(payload);
const restored = decode(bytes);bytes é um Uint8Array. Converta para Buffer quando uma API clássica do Node.js exigir.
const buffer = Buffer.from(bytes);Quando usar
- mensagens internas com contrato controlado;
- WebSocket com muitos eventos pequenos;
- cache binário;
- RPC simples;
- arquivos temporários;
- dispositivos ou redes com banda limitada.
Para APIs públicas, JSON continua mais fácil de inspecionar e integrar. Para contratos entre linguagens com evolução rígida, Avro ou Protobuf podem ser melhores.
Números inteiros
MessagePack possui tipos inteiros de 64 bits, mas JavaScript tem limite de precisão em Number. Configure a biblioteca para BigInt quando necessário.
const bytes = encode({ counter: 9007199254740995n }, {
useBigInt64: true
});
const decoded = decode(bytes, {
useBigInt64: true
});Defina uma convenção única. Misturar string, Number e BigInt para o mesmo campo gera bugs difíceis.
Dados binários
const fileChunk = Buffer.from([1, 2, 3, 4]);
const bytes = encode({ id: 'chunk-1', data: fileChunk });O formato evita Base64, reduzindo overhead. Mesmo assim, limite tamanho antes de decodificar ou armazenar.
Datas e extensões
MessagePack permite extension types para valores fora do conjunto padrão. Algumas bibliotecas oferecem timestamp integrado.
const bytes = encode({ createdAt: new Date() });Confirme o comportamento entre linguagens. Uma extensão personalizada só é interoperável quando produtores e consumidores compartilham o mesmo código e identificador.
Extension codec
import { ExtensionCodec } from '@msgpack/msgpack';
const extensionCodec = new ExtensionCodec();
extensionCodec.register({
type: 1,
encode(value) {
if (!(value instanceof URL)) return null;
return new TextEncoder().encode(value.toString());
},
decode(data) {
return new URL(new TextDecoder().decode(data));
}
});Reserve IDs de extensão e documente-os. Não execute classes ou código arbitrário durante decode.
Validação depois do decode
const value = decode(bytes);
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error('Mensagem inválida');
}
if (typeof value.type !== 'string' || typeof value.id !== 'string') {
throw new Error('Contrato inválido');
}Use JSON Schema, Zod ou validador equivalente para contratos maiores. Consulte JSON Schema no Node.js.
Versionamento
{
"type": "customer.updated.v2",
"schemaVersion": 2,
"data": { }
}MessagePack não resolve evolução sozinho. Prefira mudanças aditivas, mantenha defaults e versione mudanças incompatíveis.
WebSocket
socket.send(encode(event));
socket.onmessage = (message) => {
const event = decode(new Uint8Array(message.data));
handleEvent(event);
};Configure o socket para trabalhar com binário. Limite frame, frequência e quantidade de mensagens.
Cache
await redis.set(
cacheKey,
Buffer.from(encode(value)),
{ EX: 60 }
);Inclua versão do formato na chave para evitar decodificar dados antigos com contrato novo.
const cacheKey = `product:v2:${productId}`;Streaming
Um único objeto MessagePack precisa ser delimitado quando vários objetos são enviados em sequência. Algumas bibliotecas fornecem decoders incrementais.
for await (const value of decodeMultiStream(readable)) {
await processValue(value);
}Preserve backpressure e limite tamanho por item. Veja Streams e Backpressure no Node.js.
Framing em TCP
TCP não preserva fronteiras de mensagens. Prefixe cada payload com comprimento ou use decoder incremental.
[4 bytes length][messagepack bytes]Valide o comprimento antes de alocar memória.
Compressão
MessagePack já reduz overhead estrutural. Compressão pode ajudar em strings repetitivas e payloads grandes, mas consome CPU. Meça antes de combinar.
Desempenho
Compare com JSON usando dados reais:
- tempo de encode;
- tempo de decode;
- bytes produzidos;
- uso de CPU;
- alocações;
- latência p95;
- impacto da validação.
Para payloads pequenos, o custo de rede e I/O pode dominar.
Segurança
- limite bytes antes do decode;
- limite profundidade e coleções quando a biblioteca permitir;
- valide objeto resultante;
- bloqueie propriedades perigosas;
- não aceite extension types desconhecidos;
- não registre binário ou dados sensíveis;
- aplique timeout em streams.
Consulte Prototype Pollution no Node.js.
Interoperabilidade
Teste com todas as linguagens consumidoras. Verifique:
- int64;
- floats especiais;
- timestamps;
- binários;
- extensões;
- mapas com chaves não string;
- Unicode.
Erros de decode
Payload truncado ou inválido deve ser tratado como erro permanente, não como retry infinito. Em mensageria, encaminhe à DLQ com ID e motivo.
Observabilidade
Monitore tamanho médio, falhas de encode e decode, versão do contrato, tempo de processamento e mensagens rejeitadas. Não converta payload inteiro para JSON apenas para logging.
Testes
- round trip;
- BigInt;
- Buffer;
- Unicode;
- extension type;
- payload truncado;
- objeto profundo;
- coleção grande;
- versão desconhecida;
- interoperabilidade com outra linguagem.
Erros comuns
- achar que binário é automaticamente seguro;
- não validar schema;
- usar Number para int64;
- criar extensão sem documentação;
- não delimitar mensagens em TCP;
- decodificar sem limite;
- não versionar cache;
- escolher formato sem benchmark.
Checklist
- caso de uso justificado;
- contrato validado;
- inteiros definidos;
- extensões registradas;
- limites de bytes;
- framing correto;
- versão no envelope ou chave;
- testes entre linguagens;
- métricas de tamanho e CPU;
- fallback para mensagens inválidas.
Conclusão
MessagePack no Node.js reduz tamanho e preserva binários sem a complexidade de um schema obrigatório. Ele funciona bem em canais internos, WebSocket e caches.
A flexibilidade exige disciplina: valide o resultado, defina convenções para int64, limite payloads e versione contratos. Com benchmarks e testes de interoperabilidade, MessagePack pode diminuir tráfego sem sacrificar previsibilidade.
Consulte a especificação oficial do MessagePack e a documentação de Buffer do Node.js.




