← Back to all projects

LEARN GRPC DEEP DIVE

Learn gRPC: From Protocol Buffers to High-Performance Microservices

Goal: To deeply understand gRPC by building a complete microservices application from the ground up. You will learn not just how to use gRPC, but why it’s a powerful choice for modern systems, mastering everything from its IDL (Protocol Buffers) to advanced features like streaming, authentication, and web integration.


Why Learn gRPC?

For years, REST over JSON/HTTP/1.1 has been the de-facto standard for building APIs. However, it has limitations: text-based formats are slow, HTTP/1.1 is inefficient, and there’s no enforceable contract between server and client.

gRPC is a modern RPC framework developed by Google that addresses these problems. It uses Protocol Buffers for efficient binary serialization, runs on HTTP/2 for high-performance communication, and enables strong, typed contracts between services.

After completing these projects, you will:

  • Design efficient, typed APIs using Protocol Buffers.
  • Implement all four gRPC communication patterns: Unary, Server Streaming, Client Streaming, and Bidirectional Streaming.
  • Automatically generate client and server code from your API definitions.
  • Handle errors, deadlines, and metadata in a networked environment.
  • Implement cross-cutting concerns like authentication using interceptors.
  • Bridge your gRPC services to the traditional REST/JSON world.

Core Concept Analysis

gRPC vs. REST: A Paradigm Shift

┌────────────────────────────────────────────────────────────┐
│                        REST API Approach                     │
│                                                            │
│   Client (e.g., JavaScript)      Server (e.g., Node.js)    │
│   1. Manually craft JSON: `{"id": 123, "name": "Book"}`      │
│   2. Send POST /products ->                              │
│   3.                               <- Server parses JSON string
│   4.                                  Processes request      │
│   5.                                  Server serializes JSON │
│   6. `{"status": "ok"}`      <- Send 200 OK response       │
│                                                            │
│   Weaknesses: Text is slow, no type safety, relies on docs. │
└────────────────────────────────────────────────────────────┘
                                │
                                ▼ The gRPC Way
┌────────────────────────────────────────────────────────────┐
│                        gRPC Approach                       │
│                                                            │
│   1. Define API in .proto file (the "contract").           │
│   2. Auto-generate client and server code from .proto.     │
│                                                            │
│   Client (e.g., Go)            Server (e.g., Go)           │
│   3. Call typed function: `client.AddProduct({Id:123})` -> │
│   4.                               <- Server receives typed object
│   5.                                  No manual parsing needed │
│   6. `return &pb.Response{}`   <- Return typed object       │
│                                                            │
│   Strengths: Fast binary format, type safe, contract is code.│
└────────────────────────────────────────────────────────────┘

Key Concepts Explained

1. Protocol Buffers (Protobuf)

This is the Interface Definition Language (IDL) for gRPC. Instead of hand-writing JSON, you define your data structures (messages) and API endpoints (services) in a .proto file.

  • Messages: The data you send. Defined with typed fields (e.g., string, int32, bool).
  • Services: A collection of RPC methods.
  • protoc: The Protobuf compiler that takes your .proto file and generates code in your target language.

2. The Four Types of RPC

gRPC goes far beyond the simple request/response of REST.

  1. Unary RPC: The classic request/response. Client sends one message, server sends one back. (Like a typical REST call).
  2. Server Streaming RPC: Client sends one message, server sends back a stream of many messages. Useful for notifications.
  3. Client Streaming RPC: Client sends a stream of many messages, server sends back one. Useful for uploading large data.
  4. Bidirectional Streaming RPC: Client and server can both send streams of messages independently. Useful for interactive chat or real-time control.

3. HTTP/2 Foundation

gRPC is built on HTTP/2, which gives it several key advantages over HTTP/1.1:

  • Binary Framing: Data is sent in binary, not text, which is more efficient.
  • Multiplexing: A single TCP connection can handle multiple parallel requests and responses without blocking.
  • Streaming: HTTP/2 natively supports streaming, which makes gRPC’s streaming RPCs possible.

4. Channels, Stubs, and Services

  • Channel: A connection to a gRPC server. You create one channel and can reuse it for many calls.
  • Stub: The client-side object that is generated from your .proto file. It has methods that correspond to your service’s RPCs. When you call a method on the stub, it sends the request over the channel.
  • Service: The server-side implementation that is also generated from your .proto file. You fill in the logic for each RPC method.

Project List

We will build a simple e-commerce backend with two microservices: a Product Catalog and an Order Management service.


Project 1: Defining the API Contract with Protobuf

  • File: LEARN_GRPC_DEEP_DIVE.md
  • Main Programming Language: Protocol Buffers
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 1: Pure Corporate Snoozefest
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: API Design / Interface Definition
  • Software or Tool: protoc compiler
  • Main Book: “gRPC: Up and Running” by Kasun Indrasiri and Danesh Kuruppu

What you’ll build: A set of .proto files that define the messages and services for a Product Catalog and Order Management system.

Why it teaches gRPC: This is step zero. gRPC is a contract-first framework. Before you write a single line of application code, you must define your API. This project forces you to think about your data structures and RPC methods upfront.

Core challenges you’ll face:

  • Learning Protobuf syntax → maps to syntax = "proto3", messages, and scalar types
  • Defining service methods → maps to the service and rpc keywords
  • Choosing field numbers correctly → maps to understanding Protobuf’s binary encoding and backwards compatibility
  • Organizing .proto files → maps to using import to share message types between files

Key Concepts:

  • Protocol Buffers Language Guide: Official Google Documentation
  • gRPC Basics: Official gRPC Documentation

Difficulty: Beginner Time estimate: Weekend Prerequisites: None.

Real world outcome: You will have a set of .proto files that are the blueprint for your entire application.

// catalog.proto
syntax = "proto3";

package ecommerce;

option go_package = "ecommerce/catalog";

// The Product Catalog service definition.
service ProductCatalog {
  // Get a single product
  rpc GetProduct(GetProductRequest) returns (Product);
  // Search for products
  rpc SearchProducts(SearchProductsRequest) returns (SearchProductsResponse);
}

message Product {
  string id = 1;
  string name = 2;
  string description = 3;
  float price = 4;
}

message GetProductRequest {
  string id = 1;
}

message SearchProductsRequest {
  string query = 1;
}

message SearchProductsResponse {
  repeated Product products = 1;
}

Implementation Hints:

  1. Create a protos directory for your API definitions.
  2. Start with a catalog.proto file. Define the Product message first. Think about what fields a product needs. Use appropriate types (string, float, etc.) and assign unique field numbers starting from 1.
  3. Define the ProductCatalog service. What actions do you want to perform? Start with a simple GetProduct RPC that takes a product ID and returns a Product.
  4. Create the request and response messages for your RPCs (e.g., GetProductRequest).
  5. Install the protoc compiler and the gRPC plugin for your language of choice (e.g., protoc-gen-go-grpc). Try running the compiler on your .proto file to see the generated code. Don’t worry about using it yet, just see that it works.

Learning milestones:

  1. Your .proto files compile without errors → You understand the basic syntax of Protocol Buffers.
  2. You have defined messages and a service → You can model data and API endpoints.
  3. You have used repeated fields and import → You can handle lists of data and organize your definitions.
  4. You think about your API in terms of RPCs and Messages, not URLs and JSON.

Project 2: The Product Catalog Service (Unary RPC)

  • File: LEARN_GRPC_DEEP_DIVE.md
  • Main Programming Language: Go
  • Alternative Programming Languages: Python, Java, Node.js
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Microservices / API Implementation
  • Software or Tool: gRPC for Go, protoc
  • Main Book: “gRPC: Up and Running” by Kasun Indrasiri and Danesh Kuruppu

What you’ll build: A gRPC server in Go that implements the ProductCatalog service, and a simple command-line client to make Unary RPC calls to it.

Why it teaches gRPC: This project takes you from a .proto definition to a running client-server application. You’ll learn the fundamental workflow: generate code, implement the server interface, start the server, create a client, and make a call. This is the “Hello, World” of gRPC.

Core challenges you’ll face:

  • Generating server and client code → maps to running protoc with the correct plugins
  • Implementing the service interface → maps to creating a struct that satisfies the generated trait/interface
  • Starting the gRPC server → maps to binding to a port and registering your service
  • Creating a client and making an RPC call → maps to setting up a channel and using the client stub

Key Concepts:

  • gRPC Basics (Go): Official gRPC Go Quickstart
  • Implementing Services: The generated Go code includes an interface you must implement.
  • Channels and Stubs: Understanding the client-side components.

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 1, basic Go knowledge.

Real world outcome: You will have a running server and a client that can communicate with it.

# Terminal 1: Run the server
$ go run cmd/server/main.go
gRPC server listening on [::]:50051

# Terminal 2: Run the client
$ go run cmd/client/main.go get --id="product-123"
Product Details:
ID:    product-123
Name:  The Rust Programming Language Book
Price: $25.99

Implementation Hints:

  1. Generate the Go code from your catalog.proto file. You should get two files: catalog.pb.go (with message structs) and catalog_grpc.pb.go (with client/server interfaces).
  2. Server: Create a server struct. To implement the service, this struct must have methods that match the RPCs in your .proto file (e.g., GetProduct(...)). Embed the UnimplementedProductCatalogServer struct to ensure forward compatibility.
  3. In your server’s main function, create a net.Listener, create a new grpc.Server, register your service implementation with it (pb.RegisterProductCatalogServer), and start serving requests.
  4. Client: In your client’s main function, create a connection (channel) to the server address using grpc.Dial.
  5. Use the generated pb.NewProductCatalogClient(conn) function to create a client stub.
  6. You can now call methods on the client stub, like client.GetProduct(ctx, &pb.GetProductRequest{...}). This looks and feels like a local function call, but it’s actually a network request!

Learning milestones:

  1. Server starts and listens on a port → You can set up a basic gRPC server.
  2. Client connects to the server → You understand channels and stubs.
  3. GetProduct RPC call succeeds and returns data → You have completed a full request-response lifecycle.
  4. You feel the magic of calling a network service as if it were a local function.

Project 3: Real-Time Inventory Feeds (Server Streaming)

  • File: LEARN_GRPC_DEEP_DIVE.md
  • Main Programming Language: Go
  • Alternative Programming Languages: Python, Java, Node.js
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Real-time APIs / Streaming
  • Software or Tool: gRPC, goroutines
  • Main Book: “Concurrency in Go” by Katherine Cox-Buday

What you’ll build: You’ll add a WatchProducts RPC to your ProductCatalog service. The client will send a list of product IDs, and the server will stream back inventory level updates for those products every few seconds.

Why it teaches gRPC: This project introduces the server streaming pattern, a powerful feature unavailable in traditional REST. It forces you to think about long-lived connections and how a server can push data to a client proactively.

Core challenges you’ll face:

  • Defining a streaming RPC in Protobuf → maps to using the stream keyword on the response type
  • Implementing the server-side stream → maps to using the ServerStream object to send multiple responses
  • Handling the client-side stream → maps to looping over Recv() until the stream is closed
  • Managing the long-lived connection → maps to using goroutines and channels to simulate inventory changes

Key Concepts:

  • Server Streaming RPC: Official gRPC Go Documentation
  • Go Channels: “A Tour of Go” - Channels
  • Context handling: Cancelling the stream when the client disconnects.

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 2.

Real world outcome: Your client will connect and receive a continuous stream of updates from the server.

# Run the server...

# Run the client
$ go run cmd/client/main.go watch --ids="product-123,product-456"
Watching products...
[2025-12-20 14:30:00] Inventory Update: product-123 has 100 units.
[2025-12-20 14:30:00] Inventory Update: product-456 has 50 units.
[2025-12-20 14:30:05] Inventory Update: product-123 has 99 units.
[202C-12-20 14:30:10] Inventory Update: product-123 has 98 units.
[2025-12-20 14:30:10] Inventory Update: product-456 has 49 units.
... (client exits with Ctrl+C)
Server stream ended.

Implementation Hints:

  1. Protobuf: Add a new RPC to your ProductCatalog service: rpc WatchProducts(WatchProductsRequest) returns (stream ProductInventory);. Define the ProductInventory message.
  2. Regenerate your Go code. You’ll see the ProductCatalogServer interface now requires a WatchProducts method.
  3. Server Implementation: The WatchProducts method will receive a special stream object as an argument. You will not return from this function right away. Instead, you’ll enter a loop.
  4. Inside the loop, you can use time.Sleep to wait a few seconds. Then, create a ProductInventory message and send it to the client using stream.Send().
  5. How do you know when the client disconnects? The stream.Context() will be cancelled. Your loop should check for ctx.Err() to break gracefully.
  6. Client Implementation: After calling client.WatchProducts(), you will get a stream object back. You’ll then enter a for loop, repeatedly calling stream.Recv(). Recv() will block until a message arrives or the stream is closed by the server. When the stream is done, it will return an io.EOF error, which is your signal to exit the loop.

Learning milestones:

  1. Server sends multiple messages on a single RPC call → You understand the core of server streaming.
  2. Client successfully receives the entire stream → You can consume a server-side stream.
  3. The stream closes cleanly when the client disconnects → You understand context propagation and cancellation.

Project 4: Live Order Tracking (Bidirectional Streaming)

  • File: LEARN_GRPC_DEEP_DIVE.md
  • Main Programming Language: Go
  • Alternative Programming Languages: Python, Java, Node.js
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 4. The “Open Core” Infrastructure
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Real-time APIs / Bidirectional Communication
  • Software or Tool: gRPC, Go Channels
  • Main Book: “Concurrency in Go” by Katherine Cox-Buday

What you’ll build: A TrackOrder RPC for the OrderManagement service. The client will initiate tracking for an order and can send messages (like location pings). The server will independently stream back real-time status updates for that order (e.g., “SHIPPED”, “IN_TRANSIT”, “DELIVERED”).

Why it teaches gRPC: This demonstrates the most powerful gRPC pattern: full-duplex communication. It’s the foundation for building interactive applications like chat, collaborative editing, or live dashboards. It forces you to manage two independent streams concurrently.

Core challenges you’ll face:

  • Defining a bidi RPC → maps to using the stream keyword on both the request and response
  • Managing concurrent read and write streams → maps to using goroutines to handle receiving and sending independently
  • Correlating client messages to server state → maps to designing your application logic to be truly interactive
  • Graceful stream termination → maps to handling errors and EOF from both directions

Key Concepts:

  • Bidirectional Streaming RPC: Official gRPC Go Documentation
  • Goroutines and Channels: The primary way to manage concurrency in Go.
  • select statement: Used to wait on multiple channel operations simultaneously.

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 3.

Real world outcome: You will have an interactive client session that both sends and receives data over a single, long-lived connection.

# Run the server...

# Run the client to track an order
$ go run cmd/client/main.go track --order-id="order-xyz"
[Client] Started tracking order order-xyz. Sending location ping.
[Server] Order status update: SHIPPED
[Server] Order status update: IN_TRANSIT
[Client] Sending location ping.
[Server] Order status update: OUT_FOR_DELIVERY
[Server] Order status update: DELIVERED
[Server] Tracking complete.
Client received all updates.

Implementation Hints:

  1. Protobuf: Define the RPC: rpc TrackOrder(stream TrackOrderRequest) returns (stream TrackOrderResponse);.
  2. Server Implementation: The generated TrackOrder method will give you a stream object that can both Send and Recv. This is the core of bidi streaming.
  3. You must handle reads and writes concurrently. A common pattern is to spawn a new goroutine to handle incoming messages from the client (stream.Recv()). The main goroutine for the RPC call can then handle sending messages (stream.Send()).
  4. Use Go channels to communicate between your “read” goroutine and your “write” goroutine. For example, the read goroutine could process a client ping and push a state update onto a channel that the write goroutine is listening on.
  5. Client Implementation: Similar to the server, you will likely want to use two goroutines on the client-side: one for sending user input (stream.Send()) and one for printing server responses (stream.Recv()).
  6. Error handling is critical. If Recv() returns io.EOF, it means the client has finished sending. If Send() returns an error, the client has likely disconnected.

Learning milestones:

  1. You can send and receive a single message in both directions → You understand the bidi stream object.
  2. You can send and receive multiple messages asynchronously → You’ve correctly structured your code with goroutines.
  3. The connection closes cleanly from either client or server side → You’ve mastered stream lifecycle management.
  4. You see the potential for building highly interactive, low-latency applications.

Project 5: Securing Services with an Auth Interceptor

  • File: LEARN_GRPC_DEEP_DIVE.md
  • Main Programming Language: Go
  • Alternative Programming Languages: Python, Java
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Security / Middleware / AOP
  • Software or Tool: gRPC Interceptors, JWT library
  • Main Book: “Production-Ready Microservices” by Susan J. Fowler

What you’ll build: A gRPC Unary Interceptor that acts as middleware to protect your OrderManagement service. It will extract a JWT token from the request metadata, validate it, and reject any unauthenticated calls.

Why it teaches gRPC: Real services are not open to the public. This project teaches the idiomatic gRPC way to handle cross-cutting concerns like authentication, logging, and metrics. Interceptors are a powerful mechanism for keeping your business logic clean.

Core challenges you’ll face:

  • Writing a Unary Server Interceptor → maps to implementing the grpc.UnaryServerInterceptor function type
  • Accessing RPC metadata → maps to reading incoming headers from the request context
  • Propagating metadata from the client → maps to attaching headers to an outgoing client context
  • Returning gRPC error codes → maps to using the status package to return codes like Unauthenticated

Key Concepts:

  • gRPC Interceptors: gRPC Go Documentation, grpc.UnaryInterceptor
  • gRPC Metadata: gRPC Go Documentation, metadata.FromIncomingContext
  • JWT (JSON Web Tokens): A standard for token-based authentication.
  • gRPC Error Model: google.golang.org/grpc/status and codes.

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 2, basic understanding of authentication concepts like JWT.

Real world outcome: Unauthenticated calls to protected services will be rejected by the server.

# Client call WITHOUT a token
$ go run cmd/client/main.go add-order ...
Error creating order: rpc error: code = Unauthenticated desc = request does not contain an auth token

# Client call WITH a token
$ go run cmd/client/main.go add-order --token="valid.jwt.token" ...
Order created successfully: order-abc-789

Implementation Hints:

  1. An interceptor is just a function that wraps the execution of the actual RPC handler. Its signature looks like: func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error).
  2. Inside the interceptor:
    • Use metadata.FromIncomingContext(ctx) to get the request headers.
    • Look for the “authorization” header. If it’s not there, return an error using status.Errorf(codes.Unauthenticated, "...").
    • If the header is there, parse the JWT token (e.g., “Bearer ").
    • Validate the token using a JWT library. If invalid, return an Unauthenticated error.
    • If the token is valid, you can add the user’s identity to the context for the actual RPC handler to use.
    • Finally, call handler(ctx, req) to pass control to the next interceptor or the actual RPC method.
  3. Registering the interceptor: When you create your gRPC server, you pass the interceptor as a server option: grpc.NewServer(grpc.UnaryInterceptor(myAuthInterceptor)).
  4. Client-side: To send the token, you need to add it to the outgoing context before making the RPC call. Use metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer ...").

Learning milestones:

  1. Your interceptor logs every incoming request → You’ve successfully registered a basic interceptor.
  2. The server can extract a token sent by the client → You understand how metadata works.
  3. Unauthenticated requests are rejected with the correct error code → You have implemented a functional authentication layer.
  4. You can chain multiple interceptors (e.g., for logging and auth) → You’ve mastered gRPC middleware.

Summary

Project Main Language Difficulty
Project 1: Defining the API Contract with Protobuf Protocol Buffers Beginner
Project 2: The Product Catalog Service (Unary RPC) Go Intermediate
Project 3: Real-Time Inventory Feeds (Server Streaming) Go Intermediate
Project 4: Live Order Tracking (Bidirectional Streaming) Go Advanced
Project 5: Securing Services with an Auth Interceptor Go Advanced