151 lines
4.1 KiB
Markdown
151 lines
4.1 KiB
Markdown
# 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
|
|
|
|
```protobuf
|
|
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`
|
|
|
|
```bash
|
|
# 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`
|
|
|
|
```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`
|
|
|
|
```yaml
|
|
version: v2
|
|
modules:
|
|
- path: proto
|
|
lint:
|
|
use:
|
|
- STANDARD
|
|
breaking:
|
|
use:
|
|
- FILE
|
|
```
|
|
|
|
### Common `buf` commands
|
|
|
|
```bash
|
|
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:
|
|
|
|
```go
|
|
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.
|