Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Protobuf no Node.js

Atualizado em: 14 de setembro de 2026

Rack de servidores processando fluxos de dados no Node.js

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 protobufjs

Carregue 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:

  • int32 e int64;
  • uint32 e uint64;
  • sint32 e sint64 para negativos eficientes;
  • fixed32 e fixed64;
  • float e double.

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/*.proto

Depois gere declarations:

npx pbts \
  -o src/generated/orders.d.ts \
  src/generated/orders.js

Fixe 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 generate

O 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:

  1. publique leitores novos;
  2. mantenha valores no range antigo;
  3. aguarde migração;
  4. 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.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita