Protobuf messages

gRPC · Essentials · 12 min

Messages are the data model of every gRPC API.

A message is a typed record. Every field has a type, a name and a field number:

message User {
  int64 id = 1;
  string email = 2;
  repeated string roles = 3;
  map<string, string> labels = 4;
  Status status = 5;
  optional string nickname = 6;
}
enum Status { STATUS_UNSPECIFIED = 0; STATUS_ACTIVE = 1; }

Key rules in proto3:

  • The field number, not the name, identifies a field on the wire. Numbers 1 to 15 take one byte to encode, so give them to frequent fields. Never change a number once in use.
  • Scalars include int32, int64, uint32, sint64 (efficient for negatives), double, bool, string (UTF-8), bytes.
  • Unset fields read as default values (0, "", false, first enum value). Without optional, you cannot tell "unset" from "set to the default"; with optional you get presence (has_ methods or undefined in JS).
  • repeated is a list, map<K, V> a dictionary; maps are unordered.
  • Enums must start with a zero value, conventionally X_UNSPECIFIED = 0.

Messages can nest and reference other messages. Use package to namespace types, and import other .proto files, including Google's well-known types such as google.protobuf.Timestamp and Duration.

Example

syntax = "proto3";
package shop.v1;

import "google/protobuf/timestamp.proto";

message Order {
  string id = 1;
  repeated LineItem items = 2;
  OrderStatus status = 3;
  google.protobuf.Timestamp created_at = 4;
  optional string coupon = 5;     // presence: unset vs ""
  map<string, string> labels = 6;
}

message LineItem {
  string sku = 1;
  int32 quantity = 2;
  int64 price_cents = 3;          // money as integer cents
}

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

Remember

  • Field numbers identify fields on the wire; never reuse or change them
  • Unset scalars read as defaults; optional adds presence
  • Enums start with an UNSPECIFIED zero value
  • Use well-known types like Timestamp and Duration

Watch out for

  • Using float/double for money; use integer minor units or a decimal type.

Check yourself

  1. A proto3 `int32 count = 1;` field (no `optional`) was never set. What does the receiver read?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: protobuf.dev/programming-guides/proto3/