gRPC Cheatsheet
Service Definitions
Use this gRPC reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Service Block Syntax
syntax = "proto3"; package myapp.v1; service UserService { rpc GetUser (GetUserRequest) returns (GetUserResponse); rpc ListUsers (ListUsersRequest) returns (stream UserResponse); rpc CreateUsers (stream CreateUserRequest) returns (CreateUsersResponse); rpc Chat (stream ChatMessage) returns (stream ChatMessage); }
One service block per logical domain. Multiple services per .proto file are allowed but typically one per file keeps things maintainable.
All Four RPC Patterns
service DataService {
// 1. Unary — one request, one response
rpc GetItem (GetItemRequest) returns (Item);
// 2. Server streaming — one request, many responses
rpc ListItems (ListItemsRequest) returns (stream Item);
// 3. Client streaming — many requests, one response
rpc UploadItems (stream Item) returns (UploadSummary);
// 4. Bidirectional streaming — many requests, many responses
rpc SyncItems (stream Item) returns (stream SyncEvent);
}Request and Response Messages
// Convention: name messages after the RPC they serve message GetUserRequest { int32 user_id = 1; } message GetUserResponse { User user = 1; } // Streaming endpoints — same message type for each chunk message ChatMessage { string sender = 1; string content = 2; int64 sent_at = 3; }
Wrap scalars in messages — returning
stringdirectly is not allowed. Always wrap in a message. Usegoogle.protobuf.StringValueonly when you need explicit nullability; otherwise define a dedicated response message.
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Package | lowercase.v1 | myapp.v1 |
| Service | PascalCase + Service suffix | UserService |
| RPC method | PascalCase verb-noun | GetUser, ListOrders, StreamEvents |
| Message | PascalCase | GetUserRequest, UserResponse |
| Field | snake_case | user_id, created_at |
| Enum type | PascalCase | UserStatus |
| Enum value | UPPER_SNAKE_CASE prefixed with type | USER_STATUS_ACTIVE |
| File name | snake_case.proto | user_service.proto |
Service Options
import "google/api/annotations.proto"; // for HTTP transcoding service UserService { option deprecated = true; // mark whole service deprecated rpc GetUser (GetUserRequest) returns (GetUserResponse) { option deprecated = true; // mark one method deprecated // HTTP/JSON transcoding (requires grpc-gateway) option (google.api.http) = { get: "/v1/users/{user_id}" }; } rpc CreateUser (CreateUserRequest) returns (User) { option (google.api.http) = { post: "/v1/users" body: "*" }; } }
HTTP Transcoding Patterns (grpc-gateway)
import "google/api/annotations.proto"; service BookService { rpc GetBook (GetBookRequest) returns (Book) { option (google.api.http) = { get: "/v1/books/{book_id}" }; } rpc CreateBook (CreateBookRequest) returns (Book) { option (google.api.http) = { post: "/v1/books"; body: "book" }; } rpc UpdateBook (UpdateBookRequest) returns (Book) { option (google.api.http) = { patch: "/v1/books/{book.id}"; body: "book" }; } rpc DeleteBook (DeleteBookRequest) returns (google.protobuf.Empty) { option (google.api.http) = { delete: "/v1/books/{book_id}" }; } rpc ListBooks (ListBooksRequest) returns (ListBooksResponse) { option (google.api.http) = { get: "/v1/books" }; // query params map from request fields automatically } }
Pagination Pattern
message ListUsersRequest {
int32 page_size = 1; // max items per page (client hint)
string page_token = 2; // opaque cursor from previous response
string filter = 3; // optional filter expression
string order_by = 4; // e.g. "created_at desc"
}
message ListUsersResponse {
repeated User users = 1;
string next_page_token = 2; // empty = last page
int32 total_size = 3; // optional total count
}Long-Running Operations Pattern
import "google/longrunning/operations.proto"; service JobService { rpc StartExport (StartExportRequest) returns (google.longrunning.Operation); rpc GetOperation (google.longrunning.GetOperationRequest) returns (google.longrunning.Operation); }
Field Mask Pattern (Partial Updates)
import "google/protobuf/field_mask.proto"; message UpdateUserRequest { User user = 1; google.protobuf.FieldMask update_mask = 2; // paths: ["name", "email"] }
Multi-File Organization
protos/
myapp/v1/
user.proto // User message + UserService
order.proto // Order message + OrderService
common.proto // shared messages (Address, Money, etc.)// order.proto import "myapp/v1/common.proto"; // import shared types message Order { int32 id = 1; Money total = 2; // Money from common.proto Address address = 3; }
Versioning Strategy
// v1 stable API package myapp.v1; // protos/myapp/v1/user.proto // v2 breaking changes go in a new package + directory package myapp.v2; // protos/myapp/v2/user.proto
Bump to v2 only on breaking changes (removed fields, changed semantics). Adding fields, methods, or enum values is backward-compatible and does not require a version bump.
Common Patterns: Empty, Scalar Wrappers
import "google/protobuf/empty.proto"; import "google/protobuf/wrappers.proto"; service PingService { // Use Empty instead of defining empty messages rpc Ping (google.protobuf.Empty) returns (google.protobuf.Empty); rpc Count (google.protobuf.Empty) returns (google.protobuf.Int32Value); }
Proto Linting (buf)
# buf.yaml (v2 config format; migrate old configs with `buf config migrate`) version: v2 lint: use: - STANDARD # v2 name for the default rule set (field naming, enum prefixes, etc.) except: - PACKAGE_VERSION_SUFFIX # if you don't version packages breaking: use: - FILE
buf lint # lint all .proto files buf breaking --against '.git#branch=main' # check for breaking changes