4.1 KiB
4.1 KiB
Protobuf & Code Generation Reference
Directory Layout
Organize proto files by domain with versioned directories. Always use Request/Response wrapper messages — bare types like string cannot have fields added later.
proto/
├── user/v1/
│ ├── user.proto # Messages
│ └── user_service.proto # Service RPCs
├── order/v1/
│ ├── order.proto
│ └── order_service.proto
└── shared/v1/
└── common.proto # Pagination, timestamps, shared enums
Proto File Conventions
syntax = "proto3";
package mycompany.user.v1;
option go_package = "github.com/mycompany/myservice/gen/user/v1;userv1";
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
}
message GetUserRequest {
string user_id = 1;
}
message GetUserResponse {
User user = 1;
}
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
}
message ListUsersResponse {
repeated User users = 1;
string next_page_token = 2;
}
go_package conventions
- Format:
"import/path;alias"— the alias becomes the Go package name - Convention: lowercase version suffix (e.g.
userv1,orderv1) - Generate into a
gen/directory to keep generated code separate from hand-written code
Code Generation with protoc
# Basic generation
protoc --go_out=gen --go_opt=paths=source_relative \
--go-grpc_out=gen --go-grpc_opt=paths=source_relative \
proto/user/v1/*.proto
# With validation (using buf-validate)
protoc --go_out=gen --go_opt=paths=source_relative \
--go-grpc_out=gen --go-grpc_opt=paths=source_relative \
--validate_out="lang=go:gen" \
proto/user/v1/*.proto
# Include external imports
protoc -I proto -I third_party \
--go_out=gen --go_opt=paths=source_relative \
--go-grpc_out=gen --go-grpc_opt=paths=source_relative \
proto/user/v1/*.proto
Common protoc flags
| Flag | Purpose |
|---|---|
--go_out=DIR |
Output directory for message types |
--go-grpc_out=DIR |
Output directory for service stubs |
--go_opt=paths=source_relative |
Place output relative to proto source |
-I DIR |
Add import path for proto dependencies |
--descriptor_set_out=FILE |
Emit binary descriptor (for reflection) |
Code Generation with buf
buf is the recommended modern alternative to raw protoc. It manages dependencies, lints protos, and generates code from a single config.
buf.gen.yaml
version: v2
plugins:
- remote: buf.build/protocolbuffers/go
out: gen
opt: paths=source_relative
- remote: buf.build/grpc/go
out: gen
opt: paths=source_relative
buf.yaml
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
breaking:
use:
- FILE
Common buf commands
buf generate # Generate code from buf.gen.yaml
buf lint # Lint proto files
buf breaking --against '.git#branch=main' # Check backward compatibility
buf dep update # Update dependencies
buf build # Validate proto files compile
Generated Code Patterns
After generation, import and use the generated code:
import (
userv1 "github.com/mycompany/myservice/gen/user/v1"
)
// Server: implement the interface
type userServer struct {
userv1.UnimplementedUserServiceServer
}
// Client: use the generated client
client := userv1.NewUserServiceClient(conn)
resp, err := client.GetUser(ctx, &userv1.GetUserRequest{UserId: "123"})
Always embed Unimplemented*Server (not Unsafe*Server) — it provides forward compatibility when new RPCs are added to the proto definition.