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"; withoptionalyou get presence (has_methods orundefinedin JS). repeatedis 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;
optionaladds presence - Enums start with an UNSPECIFIED zero value
- Use well-known types like Timestamp and Duration
Watch out for
- Using
float/doublefor money; use integer minor units or a decimal type.