O Protobuf no Node.js permite definir contratos binários em arquivos .proto e gerar ou carregar tipos usados por aplicações, serviços gRPC e pipelines de eventos. Protocol Buffers usa números de campo no wire format, o que torna as mensagens compactas e permite adicionar campos sem quebrar consumidores antigos.
O formato é útil quando várias linguagens compartilham o mesmo contrato, quando o volume de mensagens é alto ou quando a equipe precisa de geração de código e evolução controlada. Porém, números de campo, presença, enums e tipos inteiros exigem disciplina: reutilizar um número removido pode causar corrupção silenciosa.
Neste guia, você aprenderá a escrever arquivos proto3, usar protobuf.js no Node.js, codificar e decodificar mensagens, gerar tipos TypeScript, trabalhar com int64, optional, enums, oneof, maps, evolução e integração com Kafka, Schema Registry e gRPC.
O que é Protocol Buffers?
Protocol Buffers é um formato de serialização desenvolvido pelo Google. A documentação oficial de proto3 define mensagens, campos, números, enums, maps, oneof, serviços e regras de evolução.
Um arquivo básico:
syntax = "proto3";
package codigofacil.orders.v1;
message OrderCreated {
string order_id = 1;
string customer_id = 2;
int64 total_cents = 3;
string occurred_at = 4;
}Por que os números importam?
O número identifica o campo no wire format. Depois que o contrato é usado, ele não deve mudar:
string order_id = 1;Renomear order_id mantendo o número pode ser compatível no binário. Trocar o número equivale a remover o campo e adicionar outro.
Nunca reutilize números
Ao remover um campo:
message OrderCreated {
reserved 5;
reserved "legacy_status";
string order_id = 1;
}Reservar número e nome impede que outra pessoa os reutilize no futuro. A documentação alerta que reutilização pode causar parse incorreto, vazamento de dados ou corrupção.
Instalação com protobuf.js
npm install protobufjsCarregue o arquivo:
import protobuf from 'protobufjs';
const root = await protobuf.load(
'proto/orders/v1/order_created.proto'
);
const OrderCreated = root.lookupType(
'codigofacil.orders.v1.OrderCreated'
);Validando uma mensagem
const payload = {
orderId: 'order-42',
customerId: 'customer-7',
totalCents: 15990,
occurredAt: new Date().toISOString()
};
const error = OrderCreated.verify(payload);
if (error) {
throw new Error(error);
}protobuf.js normalmente converte nomes snake_case do proto para camelCase no objeto JavaScript, dependendo das opções usadas.
Codificando
const message = OrderCreated.create(payload);
const buffer = OrderCreated.encode(message).finish();O resultado é um Uint8Array. Converta para Buffer quando necessário:
const nodeBuffer = Buffer.from(buffer);Decodificando
const decoded = OrderCreated.decode(nodeBuffer);
const object = OrderCreated.toObject(decoded, {
longs: String,
enums: String,
defaults: true
});As opções de conversão são importantes para int64, enums e defaults.
int64 no JavaScript
JavaScript não representa com segurança todos os inteiros de 64 bits. Bibliotecas podem usar objetos Long, string ou BigInt. Para IDs e valores grandes, converter para string evita perda:
const object = OrderCreated.toObject(decoded, {
longs: String
});Se total_cents nunca ultrapassa o limite seguro, Number pode funcionar, mas documente a restrição.
Tipos numéricos
Protobuf oferece:
int32eint64;uint32euint64;sint32esint64para negativos eficientes;fixed32efixed64;floatedouble.
Escolha pelo domínio e pela distribuição dos valores, não apenas pelo tamanho máximo.
Presença de campos
Em proto3, prefira optional quando precisa distinguir “não informado” de valor padrão:
message CustomerUpdated {
string customer_id = 1;
optional string display_name = 2;
}Sem presença explícita, uma string vazia pode representar tanto ausência quanto valor definido como vazio.
Defaults
Quando um campo não está no wire:
- string retorna vazia;
- bool retorna false;
- número retorna zero;
- enum retorna o primeiro valor;
- repeated retorna lista vazia;
- map retorna mapa vazio.
Não use zero como valor de negócio quando precisa distinguir ausência.
Enums
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_CREATED = 1;
ORDER_STATUS_PAID = 2;
ORDER_STATUS_CANCELLED = 3;
}O primeiro valor deve ser zero e sem significado de negócio específico. Isso torna o default seguro.
Adicionando valores de enum
Adicionar um valor é wire-safe, mas pode quebrar código com switch exaustivo. Consumidores devem tratar valores desconhecidos.
Repeated
message Order {
string id = 1;
repeated OrderItem items = 2;
}Repeated preserva a ordem. Tipos numéricos usam packed encoding por padrão em proto3.
Maps
message EventEnvelope {
map<string, string> metadata = 1;
}A ordem de maps não é garantida. Não use a ordem para assinatura, hashing ou lógica de negócio.
oneof
message PaymentMethod {
oneof method {
Card card = 1;
Pix pix = 2;
BankSlip bank_slip = 3;
}
}Apenas um campo fica ativo. Definir outro limpa o anterior. Mover vários campos existentes para oneof pode ser incompatível.
Mensagens aninhadas
message Order {
message Item {
string product_id = 1;
int32 quantity = 2;
}
repeated Item items = 1;
}Evite arquivos gigantes com dezenas de mensagens não relacionadas. A própria documentação recomenda limitar dependências.
Imports
import "google/protobuf/timestamp.proto";
message OrderCreated {
google.protobuf.Timestamp occurred_at = 1;
}Configure proto_path corretamente no compilador.
Well-known types
Tipos oficiais incluem:
- Timestamp;
- Duration;
- Any;
- Struct;
- wrappers;
- FieldMask.
Use Any com moderação; ele reduz visibilidade do contrato.
Geração estática
protobuf.js pode gerar código:
npx pbjs \
-t static-module \
-w es6 \
-o src/generated/orders.js \
proto/orders/v1/*.protoDepois gere declarations:
npx pbts \
-o src/generated/orders.d.ts \
src/generated/orders.jsFixe versões e execute no CI.
ts-proto
Outra opção é gerar TypeScript com ts-proto. Ele oferece tipos e helpers modernos, mas adiciona decisões de configuração. Escolha uma ferramenta e mantenha o output gerado fora de edições manuais.
buf
Buf oferece lint, breaking change detection e geração:
buf lint
buf breaking --against '.git#branch=main'
buf generateO projeto oficial Buf ajuda a tratar schemas como contratos versionados.
Integração com gRPC
Arquivos proto podem definir serviços:
service OrdersService {
rpc GetOrder(GetOrderRequest)
returns (GetOrderResponse);
}Para RPC completo, consulte gRPC com Node.js.
Integração com Kafka
Protobuf também pode serializar eventos Kafka. Publique bytes com KafkaJS e use Schema Registry para gerenciar versões:
Schema Registry e Protobuf
O registry armazena schemas, referências e IDs. O produtor codifica usando um schema registrado e o consumidor decodifica pelo ID. Defina compatibility mode e valide no pipeline.
ProtoJSON
Protobuf possui mapeamento JSON canônico. Ele é útil para gateways e debugging, mas tem regras diferentes do binário. Alterar nome de campo pode ser wire-safe no binário e incompatível no JSON.
Unknown fields
Consumidores antigos preservam campos desconhecidos no binário em implementações modernas. Porém, converter para JSON ou copiar campo por campo pode perder essas informações.
Mudanças wire-safe
- adicionar campo novo;
- remover campo reservando número;
- adicionar valor de enum, com cuidado no código;
- adicionar mensagem nova;
- adicionar serviço ou método independente.
Mudanças perigosas
- trocar número de campo;
- reutilizar número removido;
- trocar tipo incompatível;
- mover vários campos para oneof;
- alterar semântica mantendo o número;
- renomear sem considerar JSON.
Compatibilidade condicional
Algumas trocas, como int32 para int64, são compatíveis no wire, mas podem perder dados enquanto consumidores antigos ainda existem. Faça rollout em fases:
- publique leitores novos;
- mantenha valores no range antigo;
- aguarde migração;
- libere valores maiores.
Versionamento de packages
package codigofacil.orders.v1;Quando uma mudança não pode ser compatível, crie v2 e ofereça migração. Não use versionamento como desculpa para criar uma nova versão a cada campo.
Testes de round trip
const encoded = OrderCreated.encode(message).finish();
const decoded = OrderCreated.decode(encoded);
assert.equal(decoded.orderId, 'order-42');Fixtures antigas
Armazene bytes de versões anteriores e decodifique com o schema atual. Isso detecta breaking changes que compilação isolada não encontra.
Testes de contrato
Valide produtor e consumidor no CI. Para serviços, veja Contract Testing com Pact. Para Protobuf, Buf breaking e fixtures são ferramentas importantes.
Performance
Carregue descriptors uma vez no startup. Não faça parse do arquivo proto para cada mensagem. Meça:
- tempo de encode;
- tempo de decode;
- tamanho;
- allocations;
- impacto de conversões para objeto.
Segurança
- Limite tamanho de mensagens.
- Não carregue schemas de usuários.
- Valide campos após decode.
- Evite Any sem allowlist.
- Proteja dados pessoais.
- Restrinja acesso ao registry.
- Atualize runtime e geradores.
Observabilidade
Registre message type, versão, schema ID e falha, mas não bytes completos. Métricas úteis:
- encode errors;
- decode errors;
- unknown enum values;
- payload size;
- latência;
- breaking checks no CI.
Consulte Métricas Prometheus no Node.js.
Protobuf versus Avro
Protobuf usa números de campo e geração de código. Avro usa schemas JSON e resolução explícita entre writer e reader. Protobuf combina muito bem com gRPC; Avro é tradicional em Kafka e data pipelines.
Veja Avro no Node.js.
Erros comuns
- Reutilizar field number: dados ficam ambíguos.
- int64 como Number: perde precisão.
- Enum sem zero neutro: default vira negócio.
- Editar código gerado: alterações desaparecem.
- Sem reserved: número removido volta.
- Confundir binário e JSON: compatibilidade muda.
- Parse do proto por request: performance cai.
- Sem fixtures antigas: evolução não é validada.
Conclusão
O Protobuf no Node.js oferece mensagens compactas, contratos multilíngues e evolução baseada em números de campo. Com protobuf.js ou geração estática, aplicações podem validar, codificar e decodificar dados com baixa sobrecarga.
Reserve números removidos, trate int64 corretamente, use optional quando presença importa e valide breaking changes no CI. Integrado a gRPC ou Schema Registry, Protobuf cria contratos duráveis para comunicação entre serviços e eventos.




