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

Protobuf no Node.js: schemas e compatibilidade

Atualizado em: 25 de outubro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

Protobuf no Node.js usa contratos .proto para serializar mensagens binárias compactas e gerar código entre linguagens. O formato é amplamente usado em gRPC, eventos e comunicação entre serviços porque mantém números de campo estáveis e oferece regras claras para evolução.

O schema é o centro do contrato. Nomes podem mudar internamente, mas os números de campo não devem ser reutilizados. Uma alteração aparentemente pequena pode tornar mensagens antigas ambíguas se o time não respeitar compatibilidade.

Primeiro arquivo .proto

syntax = "proto3";

package orders.v1;

message OrderCreated {
  string event_id = 1;
  string order_id = 2;
  int64 amount_cents = 3;
  string currency = 4;
}

Cada campo possui um número. Ele faz parte do formato binário e precisa permanecer estável.

Carregamento dinâmico

import protobuf from 'protobufjs';

const root = await protobuf.load('orders.proto');
const OrderCreated = root.lookupType('orders.v1.OrderCreated');

Em projetos grandes, geração estática pode reduzir custo de startup e melhorar tipagem.

Validando e codificando

const payload = {
  eventId: crypto.randomUUID(),
  orderId: 'ord-123',
  amountCents: 12990,
  currency: 'BRL'
};

const error = OrderCreated.verify(payload);
if (error) throw new Error(error);

const message = OrderCreated.create(payload);
const buffer = OrderCreated.encode(message).finish();

Decodificando

const decoded = OrderCreated.decode(buffer);
const object = OrderCreated.toObject(decoded, {
  longs: String,
  defaults: true
});

Configurar longs: String evita perda de precisão em inteiros de 64 bits. Escolha uma convenção única para toda a aplicação.

int64 e JavaScript

O tipo Number representa inteiros com segurança apenas até Number.MAX_SAFE_INTEGER. IDs, timestamps em nanossegundos e contadores podem exceder esse limite.

Opções comuns:

  • converter para string;
  • usar BigInt com biblioteca compatível;
  • usar um tipo Long;
  • modelar como string no contrato quando não há operações aritméticas.

Valores padrão no proto3

Campos escalares ausentes assumem valores padrão, como string vazia, zero e false. Isso pode dificultar distinguir “não enviado” de “enviado como zero”.

optional string coupon_code = 5;

Use optional, wrappers ou mensagens aninhadas quando presença for importante.

Enums

enum OrderStatus {
  ORDER_STATUS_UNSPECIFIED = 0;
  ORDER_STATUS_PENDING = 1;
  ORDER_STATUS_PAID = 2;
}

Reserve o valor zero para desconhecido. Consumidores precisam tolerar valores novos e não devem falhar ao receber enum que ainda não conhecem.

Mensagens aninhadas

message Money {
  int64 amount_cents = 1;
  string currency = 2;
}

message OrderCreated {
  string event_id = 1;
  string order_id = 2;
  Money amount = 3;
}

Mensagens reutilizáveis tornam o contrato mais expressivo, mas acoplamento excessivo pode fazer uma mudança afetar muitos serviços.

oneof

message PaymentMethod {
  oneof method {
    Card card = 1;
    Pix pix = 2;
  }
}

oneof garante que somente uma alternativa está ativa. Valide também regras de negócio depois da decodificação.

Evolução compatível

Práticas seguras:

  • adicione novos campos com números novos;
  • não altere o significado de campo existente;
  • não reutilize números removidos;
  • reserve números e nomes antigos;
  • tolere campos desconhecidos;
  • não torne interpretação mais restritiva sem versão.

Reservando campos

message Customer {
  reserved 3, 7 to 9;
  reserved "legacy_code";

  string id = 1;
  string name = 2;
}

Isso impede reutilização acidental durante compilação.

Mudanças de tipo

Alguns tipos possuem representação wire compatível, mas isso não significa que a semântica continue correta. Evite trocar tipo em campo existente. Crie um campo novo e migre consumidores.

Versionamento de packages

package orders.v2;

Quando a mudança é realmente incompatível, use novo package, serviço ou mensagem. Consulte API Versioning no Node.js.

gRPC

service OrderService {
  rpc GetOrder(GetOrderRequest) returns (Order);
  rpc StreamOrders(StreamOrdersRequest) returns (stream Order);
}

gRPC combina Protobuf com HTTP/2, deadlines, metadata e streaming. O contrato precisa documentar erros, idempotência e limites além das mensagens.

Eventos

Ao publicar em Kafka ou RabbitMQ, inclua metadados em headers ou envelope:

  • event ID;
  • tipo e versão;
  • timestamp;
  • tenant;
  • correlation ID;
  • schema version.

Veja Kafka no Node.js.

Determinismo

A codificação não deve ser usada como hash canônico sem confirmar modo determinístico e regras da implementação. Mapas e campos desconhecidos podem alterar ordem. Para assinatura, defina uma representação canônica ou assine bytes exatamente transmitidos.

Tamanho e limites

Mensagens binárias também podem causar abuso. Limite bytes, profundidade, quantidade de elementos e comprimento de strings antes ou durante processamento.

Campos desconhecidos

Uma vantagem do formato é que leitores antigos podem ignorar campos novos. Verifique como a biblioteca preserva ou descarta esses campos ao decodificar e recodificar; isso importa em proxies que repassam mensagens.

Geração de TypeScript

Ferramentas podem gerar tipos e clientes. Fixe versões do compilador e plugins no CI para builds reproduzíveis. Revise código gerado como artefato de contrato.

Schema Registry e Buf

Registries e ferramentas de lint podem detectar breaking changes. Mesmo sem infraestrutura dedicada, o CI deve comparar o contrato novo com o publicado.

Consulte Testes de Contrato no Node.js.

Validação de negócio

verify() confirma tipos estruturais. Depois, valide moeda, limites, permissões, estados e relacionamentos.

if (BigInt(object.amountCents) <= 0n) {
  throw new Error('Valor deve ser positivo');
}

Observabilidade

Monitore falhas de decode, tamanho de mensagens, versão do package, latência de serialização e payloads enviados à DLQ. Não registre conteúdo sensível em texto.

Testes

  • round trip;
  • campos desconhecidos;
  • novo campo com leitor antigo;
  • campo removido e reservado;
  • int64 acima do limite;
  • enum desconhecido;
  • optional ausente e vazio;
  • oneof inválido;
  • payload truncado;
  • mensagem acima do limite.

Erros comuns

  • reutilizar número de campo;
  • mudar significado sem versão;
  • converter int64 para Number;
  • confundir valor padrão com presença;
  • não tolerar enum novo;
  • não limitar tamanho;
  • gerar código com versões diferentes;
  • acreditar que schema valida negócio;
  • assinar objeto reserializado.

Checklist

  • números estáveis;
  • campos removidos reservados;
  • longs tratados;
  • presença modelada;
  • enums tolerantes;
  • limites de mensagem;
  • geração reproduzível;
  • CI detecta breaking changes;
  • validação de negócio;
  • testes entre versões.

Conclusão

Protobuf no Node.js fornece contratos compactos e interoperáveis para APIs e eventos. O formato permite evolução quando números de campo, presença e compatibilidade são tratados como parte da arquitetura.

Não reutilize campos, cuide de int64 e valide regras depois do decode. Com geração fixa, testes de contrato e limites de payload, Protobuf permite integrar serviços em várias linguagens com mudanças previsíveis.

Consulte a documentação oficial do Protocol Buffers.

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