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 string directly is not allowed. Always wrap in a message. Use google.protobuf.StringValue only when you need explicit nullability; otherwise define a dedicated response message.

Naming Conventions

ElementConventionExample
Packagelowercase.v1myapp.v1
ServicePascalCase + Service suffixUserService
RPC methodPascalCase verb-nounGetUser, ListOrders, StreamEvents
MessagePascalCaseGetUserRequest, UserResponse
Fieldsnake_caseuser_id, created_at
Enum typePascalCaseUserStatus
Enum valueUPPER_SNAKE_CASE prefixed with typeUSER_STATUS_ACTIVE
File namesnake_case.protouser_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