📝 Quiz: Part I — Foundations (Chapters 1-5)

1. What transport protocol does gRPC use?

2. Why does gRPC require HTTP/2 (not HTTP/1.1)?

3. In Protobuf, what happens when a field has its default value (e.g., int32 = 0)?

4. What was Google's internal RPC system (predecessor to gRPC) called?

5. Why does Protobuf use field numbers instead of field names?


Chapter 6: Service Definition with .proto Files

The .proto file is the heart of gRPC. It's the contract between client and server — the single source of truth that both sides agree on. This chapter teaches you how to write service definitions like a pro.

Anatomy of a gRPC Service Definition

syntax = "proto3";

package telecom.amf.v1;

import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

option go_package = "telecom/amf/v1;amfv1";

// Service: a collection of RPC methods
service SessionService {
  // Unary RPC: one request, one response
  rpc CreateSession(CreateSessionRequest) returns (Session);
  
  // Unary RPC
  rpc GetSession(GetSessionRequest) returns (Session);
  
  // Server streaming: one request, stream of responses
  rpc WatchSession(WatchSessionRequest) returns (stream SessionEvent);
  
  // Client streaming: stream of requests, one response
  rpc ReportMetrics(stream MetricReport) returns (MetricSummary);
  
  // Bidirectional streaming: stream both ways
  rpc StreamUpdates(stream SessionUpdate) returns (stream SessionEvent);
  
  // Returns nothing meaningful
  rpc DeleteSession(DeleteSessionRequest) returns (google.protobuf.Empty);
}

Request/Response Message Design

A critical gRPC convention: every RPC gets its own request and response message types, even if they seem redundant.

// ✗ BAD: Reusing messages across RPCs
rpc GetUser(UserId) returns (User);
rpc DeleteUser(UserId) returns (Empty);

// ✓ GOOD: Dedicated request/response per RPC
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);

message GetUserRequest {
  string user_id = 1;
}

message GetUserResponse {
  User user = 1;
}

message DeleteUserRequest {
  string user_id = 1;
}

message DeleteUserResponse {
  // Empty for now, but can add fields later without breaking the API
}
💡 Why Dedicated Messages?

Because APIs evolve. Today GetUser only needs an ID. Tomorrow you might need to add a field_mask (which fields to return) or a view parameter. If you used a generic UserId message, you'd have to change every RPC that uses it. With GetUserRequest, you only change one message.

Naming Conventions

ElementConventionExample
Packagelowercase dot-separatedmycompany.myservice.v1
ServicePascalCase + "Service" suffixUserService
RPC methodPascalCase verb+nounCreateUser, ListOrders
MessagePascalCaseCreateUserRequest
Fieldsnake_caseuser_id, created_at
EnumPascalCaseSessionState
Enum valueUPPER_SNAKE_CASESTATE_ACTIVE

Common Patterns

CRUD Operations

service UserService {
  rpc CreateUser(CreateUserRequest) returns (User);
  rpc GetUser(GetUserRequest) returns (User);
  rpc UpdateUser(UpdateUserRequest) returns (User);
  rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}

Resource Hierarchy

// Parent-child relationships via resource names
message GetSessionRequest {
  // Format: "networks/{network}/sessions/{session}"
  string name = 1;
}

Well-Known Types (Google's Standard Library)

import "google/protobuf/timestamp.proto";  // point in time
import "google/protobuf/duration.proto";   // time span
import "google/protobuf/empty.proto";      // void return
import "google/protobuf/field_mask.proto"; // partial updates
import "google/protobuf/wrappers.proto";   // nullable scalars
import "google/protobuf/any.proto";        // dynamic typing
import "google/protobuf/struct.proto";     // JSON-like dynamic

message Session {
  string id = 1;
  google.protobuf.Timestamp created_at = 2;
  google.protobuf.Duration ttl = 3;
}

File Organization

For a real service, organize your .proto files like this:

proto/ ├── mycompany/ │ └── myservice/ │ └── v1/ │ ├── service.proto ← service + RPC definitions │ ├── resources.proto ← shared message types (User, Session, etc.) │ └── enums.proto ← shared enums └── google/ └── protobuf/ ├── timestamp.proto ← well-known types (vendored or from include path) └── ...

Import and Package System

// File: mycompany/billing/v1/invoice.proto
syntax = "proto3";
package mycompany.billing.v1;

// Import from another package
import "mycompany/users/v1/user.proto";

message Invoice {
  string id = 1;
  // Reference type from another package using fully-qualified name
  mycompany.users.v1.User customer = 2;
}

Comments and Documentation

// Single-line comments use //

/* Multi-line comments
   use C-style blocks */

// Comments directly above a message/field/rpc become documentation
// in generated code (as doc comments in Go, Javadoc in Java, etc.)

/// Some tools recognize triple-slash as doc comments

// Create a new user account.
// The email must be unique across the system.
// Returns ALREADY_EXISTS if the email is taken.
rpc CreateUser(CreateUserRequest) returns (User);
Key Takeaways