Protocol Buffers: Defining Your Schema
๐ Protocol Buffers: Defining Your Schema
.proto file glimpse without explaining it. This chapter covers the real syntax โ messages, fields, numbering rules โ and why this schema-first contract is a genuine structural advantage over how most REST APIs document themselves.
The .proto File
Every gRPC schema starts with a syntax declaration and, optionally, a package name to avoid naming collisions across files.
Message Types & Scalar Fields
A message defines a structured data type โ each field has a type, a name, and a number.
| proto3 Scalar Type | Roughly Equivalent To |
|---|---|
int32 / int64 | Integer (32-bit / 64-bit) |
string | UTF-8 text |
bool | Boolean |
float / double | Floating-point number |
bytes | Raw binary data |
Field Numbering Rules
The number after each field's = is not a default value โ it's the field's unique identifier in the binary wire format. The field's name only exists for humans reading the .proto file; the actual bytes sent over the network reference fields by number, never by name.
- Numbers 1โ15 encode in a single byte โ reserve these for a message's most frequently set fields.
- Numbers 16 and above take two bytes to encode โ fine for less common fields.
- A field number, once used in any deployed version of a schema, should never be reused for a different field later โ old serialized data (or old clients) may still reference it.
Repeated Fields
The repeated keyword marks a field as a list โ the closest proto3 equivalent to a JSON array.
Nested Messages
A message can use another message as a field's type, building the same kind of hierarchical structure a nested JSON object would.
Why Schema-First Beats REST's Often-Implicit Contract
A REST API's contract is frequently documented separately from the code that implements it โ an OpenAPI/Swagger spec written by hand, generated after the fact, or simply a wiki page โ and any of these can silently drift out of sync with what the API actually does, exactly the kind of documentation-vs-reality gap api-design1-4 touched on. A .proto file has no such gap: it's not documentation about the contract, it is the contract โ the exact same file that generates both the client and server code (Chapter 3), so client and server can never silently disagree about a message's shape.
REST's Contract vs. gRPC's Contract
REST (typically)
Documented separately (OpenAPI/Swagger, a wiki) โ can drift from the real implementation over time if not diligently kept in sync.
gRPC
The .proto file generates both sides of the contract directly โ client and server code both come from the same source, so they can't quietly diverge.
๐ป Coding Challenges
Challenge 1: Write a Message Definition
Write a .proto message called Order with fields: id (int32), customer_name (string), and a repeated list of item_names (string). Assign field numbers deliberately.
Goal: Practice combining scalar fields and a repeated field in one message definition.
Challenge 2: Add a Nested Message
Extend Challenge 1's Order message with a nested ShippingInfo message (fields: address string, expedited bool) as a field on Order.
Goal: Practice defining and referencing a nested message type.
Challenge 3: Diagnose a Field Numbering Mistake
A team removes a deprecated field numbered 3 from a message, then later adds a completely different new field and assigns it number 3 to "reuse" it. Explain what could go wrong.
Goal: Practice applying this chapter's field-numbering rule to a realistic schema-evolution mistake.
Because the wire format identifies fields by number, not name, renaming a field (e.g. name โ full_name, keeping the same number) is completely safe โ old and new code still agree on what number 2 means. But changing a field's number, or reusing a retired number for something unrelated, is genuinely dangerous: any client or stored data still using the old schema will misinterpret the field, potentially reading a string where it expects an integer, with no error raised at all. When a field is truly no longer needed, mark its number reserved rather than letting it be reassigned, so no one accidentally reuses it later.
๐ฏ What's Next
The next chapter is Generating Code & Your First gRPC Service โ the protoc compiler, generated client/server stubs, service definition syntax, and a basic unary call end to end.