Working with Advanced Proto Features
1. Implementing Custom Options
Example: Defining and using a custom field option
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions {
string format = 50001;
}
message User {
string email = 1 [(format) = "email"];
}
| Extension Target | Use |
|---|---|
FileOptions | File-wide config |
MessageOptions | Per-message |
FieldOptions | Per-field (validation, docs) |
MethodOptions | Per-RPC (HTTP rules, auth) |
2. Using Message Extensions
| Note | Detail |
|---|---|
| Status | proto2 feature; in proto3 only for *Options |
| Modern alternative | Use Any or oneof |
3. Working with Unknown Fields
| Behavior | Detail |
|---|---|
| Default | Preserved by parser, re-emitted on serialize |
| Access (Go) | m.ProtoReflect().GetUnknown() |
| Purpose | Forward compatibility across versions |
4. Using Proto Reflection API
Example: Iterate fields via reflection (Go)
m := &User{Id: "u1", Email: "a@b.com"}
m.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
fmt.Println(fd.Name(), v.Interface())
return true
})
| Interface | Purpose |
|---|---|
ProtoReflect() | Access descriptors at runtime |
| Descriptor | Schema introspection |
| FieldDescriptor | Per-field metadata |
5. Implementing Dynamic Messages
| API (Go) | Purpose |
|---|---|
dynamicpb.NewMessage(desc) | Build messages without generated code |
protoregistry.GlobalFiles | Lookup descriptors at runtime |
| Use case | Generic proxies, transcoders, gateways |
6. Using JSON Mapping
| Proto | JSON |
|---|---|
| snake_case field | camelCase by default (user_id → userId) |
| enum value | String name; integer also accepted |
| bytes | Base64 |
| Timestamp/Duration | RFC 3339 / duration string |
| Default fields | Omitted unless EmitUnpopulated=true |
7. Configuring Field Behavior
| Annotation (google.api.field_behavior) | Meaning |
|---|---|
| REQUIRED | Server rejects if unset |
| OPTIONAL | May be unset |
| OUTPUT_ONLY | Server-populated, client cannot set |
| INPUT_ONLY | Sent on input, not returned |
| IMMUTABLE | Cannot be changed after creation |
8. Using API Annotations
Example: HTTP mapping annotation
import "google/api/annotations.proto";
service UserService {
rpc GetUser(GetUserRequest) returns (User) {
option (google.api.http) = { get: "/v1/users/{id}" };
}
}
| Option | Purpose |
|---|---|
google.api.http | REST mapping (grpc-gateway) |
google.api.method_signature | Client method overloads |
9. Implementing Proto Validation
| Tool | Purpose |
|---|---|
| protoc-gen-validate (PGV) LEGACY | Original validator |
| protovalidate MODERN | CEL-based, language-agnostic successor |
| Buf plugin | buf.build/bufbuild/protovalidate |
10. Using Service Options
Example: Default host + OAuth scope
service UserService {
option (google.api.default_host) = "users.googleapis.com";
option (google.api.oauth_scopes) = "https://www.googleapis.com/auth/users";
}
| Option | Purpose |
|---|---|
deprecated = true | Mark service/method deprecated |
idempotency_level | NO_SIDE_EFFECTS / IDEMPOTENT |