Salient Points for Protocol Buffers

Simply Explained (for Golang)

Mipsmonsta
3 min readFeb 17, 2022

How to start .proto file?

syntax = "proto3";

Without the line above, the complier for protocol buffer will assume the syntax is based on proto2.

Specify Field Types

All fields are scalar type: string, float, int32, int64, uint32, uint64, sint32, sint64, fixed32 (always 4 bytes), fixed64 , bool, string, bytes etc.

message Foo { //this is a message type
string query = 1;
int64 large_number = 2;
bool is_bar = 3;
bool is_foo = 4;
}

Notice the use of underscore as convention for the field names. You can also have multiple message types in the same .proto file. Notice message name starts in big cap.

Default values

strings ----> empty string
bytes ------> empty bytes
bool -------> false
numeric types --> zero
enums ------> default value is the first defined enum value, which
must be 0

Composite Types — Enum

message SearchRequest {
...
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
}
}

Every enum definition must contain a constant that maps to zero as its first element. This is so that the default value aforementioned can work.

If the same enum constants are used in other enum definition, you need the line: option allow_alias = true;

message SearchResponse {
...
enum EnumAllowinAlias {
option allow_alias = true;
UNIVERSAL = 0; //already used in previous enum Corpus
}
}

Including Maps in definitions

message SearchWithMapParams {
map<string, Project> projects = 5; // where Project is a message
}

Handling removal of fields

When you remove a field, you need to prevent the field numbers from being reused. What you need to do is to create reserved fields to “block” these field numbers. If not done, when other reuse the field numbers, severe compatibility can result.

message Foo {
string query = 1;
reserved 2, 3 to 4;
reserved "is_bar", "is_foo", "large_number";
}

Nesting other message types

Other messages can be included as field types i.e. nested. For example:

message BigBar {
repeated NestedBar inner_bar = 1;
}
message NestedBar {
int32 weight = 1;
string brand = 2;
repeated string packaging = 3;
}

Here, the repeated keywork ensures you can use other messages as field types inside your message.

More nesting…

You can fall in love with nesting deeply :P

message BigBar {
message NestedBar {
int32 weight = 1;
string brand = 2;
repeated string packaging = 3;
}
repeated NestedBar a_bar = 1; //repeated key again
}

Furthermore…

message Outer {
message Middle {
message Inner {
int64 something = 1;
string another = 2;
}
}
}

Importing .proto files

To help in nesting, you can import definitions from other .proto files

import "otherproject/some_protos.proto";

The protocol compiler searches for imported files in a set of folders specified on the protocol CLI using the flag,

-I or --proto_path

Placeholder .proto file

Imagine if you need to move your .proto file to a new file location. All your other .proto files importing the .proto file will need to be updated with a new import line. That will be a lot of work. Is there a better way?

To save the day, you can use the “import public” syntax such as —

new proto file:

//foo.proto
//All definitions of messages are moved here

old proto file to be modified:

//bar.proto
import public "foo.proto" //this line is added

other proto file using bar.proto:

//client.proto
import "old.proto"; //continue to import the old proto file

Oneof fields

If you have mutiple fields in a message and at most one field will be set at the same time, you can enforce filling one field by using the oneof feature.

message SlimMessage {  oneof a_name {
string this = 1;
bool or_that = 4;
}
}

Defining Services in .proto file

service AService {
rpc Provide(Request) returns (Response);
}

Compiling to .pb.go

In go, the protobuf go compiler generates .pb.go file with a type for each message type in your file.

Generating your classes in go

protoc --proto_path=IMPORT_PATH --go_out=DST_DIR path/to/file.proto

GRPC support in go

If you are using grpc, you also need to generate the grpc go files with an additioanl flag of “ go-grpc_out=.”

protoc -I=. --go_out=./chunky --go-grpc_out=./chunky  ./chunky/chunk.proto

Above example generates the file into the relative folder of ./chunky

--

--

Mipsmonsta

Writing to soothe the soul, programming to achieve flow