Skip to content

Repeated, Maps & Well-Known

Add repeated to a field and it becomes an ordered list of that type — zero or more values:

message Playlist {
string name = 1;
repeated string songs = 2; // 0..N strings
repeated Track tracks = 3; // 0..N messages
}

Repeated fields preserve order, and an empty list is just the default — you never send “null vs empty,” they’re the same. For numeric scalar lists, protobuf uses packed encoding by default in proto3, storing the values back-to-back without a tag per element, which keeps large numeric arrays compact.

A map is syntactic sugar for a repeated key/value entry, giving you an associative container:

message Config {
map<string, string> labels = 1;
map<int64, User> user_by_id = 2;
}

Keys can be any integer, bool, or string type; values can be any type except another map. Two caveats worth knowing: map ordering is not guaranteed on the wire, and maps can’t be repeated (wrap them in a message if you need a list of maps).

Well-known types: protobuf’s standard library

Section titled “Well-known types: protobuf’s standard library”

You don’t model time, duration, or “any value” from scratch. The google.protobuf package ships well-known types — import and use them.

flowchart LR
  time["Timestamp / Duration
→ points in time & spans"]
  wrap["Int32Value, StringValue…
→ nullable scalars"]
  any["Any / Struct
→ dynamic / unknown shapes"]
  util["Empty / FieldMask
→ no-arg calls & partial updates"]
The well-known types you'll actually reach for

The ones you’ll use constantly:

  • Timestamp and Duration — a precise point in time and a span. Always prefer these over a raw int64 of “milliseconds since something.”
  • Wrappers (Int32Value, StringValue, BoolValue, …) — a message-typed box around a scalar, which gives you nullability. An older alternative to the optional keyword; still common in existing APIs.
  • Any — carries an arbitrary serialized message plus its type URL, for genuinely dynamic payloads. Powerful but it defeats static typing, so use it sparingly.
  • Struct — arbitrary JSON-like data (objects, lists, values) when a shape truly isn’t known ahead of time.
  • Empty — a message with no fields, for RPCs that take or return nothing.
  • FieldMask — a list of field paths, the standard way to express “update only these fields” in a partial-update API.
import "google/protobuf/timestamp.proto";
import "google/protobuf/field_mask.proto";
message Event {
string name = 1;
google.protobuf.Timestamp created_at = 2;
}
message UpdateUserRequest {
User user = 1;
google.protobuf.FieldMask update_mask = 2; // which fields to change
}
What does adding `repeated` to a field do?
Which is true about protobuf maps?
How should you represent a point in time in a message?
What is `google.protobuf.FieldMask` used for?