gRPC is the high-performance alternative to REST that powers communication at Google, Netflix, and Uber. It uses Protocol Buffers (protobuf) for compact binary serialization and HTTP/2 for multiplexed streams — making it 3-10x faster than JSON over HTTP/1.1. This guide covers everything from protobuf basics to production patterns for gRPC services.

gRPC vs REST: When to Choose What

FactorgRPCREST (JSON)
ProtocolHTTP/2 (multiplexed, binary)HTTP/1.1 or HTTP/2 (text-based JSON)
SerializationProtocol Buffers (binary, 3-10x smaller)JSON (text, human-readable)
Contract.proto files (strongly typed, code-generated)OpenAPI/Swagger (optional, often manual)
StreamingBuilt-in: unary, server, client, bidirectionalServer-Sent Events, WebSocket (separate setup)
Browser SupportLimited (needs gRPC-Web proxy)Native (fetch, XMLHttpRequest)
DebuggingHarder (binary, needs grpcurl or BloomRPC)Easy (curl, browser DevTools, Postman)
Performance3-10x faster, 3-10x smaller payloadGood enough for most use cases
Best ForInternal microservices, high-throughput RPCPublic APIs, browser-to-server communication

Defining a gRPC Service in Protobuf

// users.proto — a complete gRPC service definition
syntax = "proto3";

package users.v1;

// Request/Response messages
message GetUserRequest {
  string user_id = 1;  // Field numbers (1, 2, 3...) determine wire format
}

message User {
  string user_id = 1;
  string email = 2;
  string display_name = 3;
  repeated string roles = 4;  // repeated = array/list
  optional string avatar_url = 5;  // optional = can be unset
}

message ListUsersRequest {
  int32 page_size = 1;
  string page_token = 2;
}

message ListUsersResponse {
  repeated User users = 1;
  string next_page_token = 2;
}

// The service: what RPCs are available
service UserService {
  // Unary: one request → one response
  rpc GetUser(GetUserRequest) returns (User);

  // Server streaming: one request → many responses
  rpc ListUsers(ListUsersRequest) returns (stream User);

  // Client streaming: many requests → one response
  rpc BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateUsersResponse);

  // Bidirectional streaming: many ↔ many
  rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}

// Generate code: protoc --go_out=. --go-grpc_out=. users.proto
// For Node.js: @grpc/grpc-js + @grpc/proto-loader

gRPC Streaming Patterns

PatternUse CaseExample
Unary (1 req → 1 resp)Standard API callsGetUser(id), CreateOrder(order)
Server Streaming (1 req → N resp)Large result sets, real-time feedsListUsers (stream results), SubscribeToEvents
Client Streaming (N req → 1 resp)Uploading data, batchingUploadFile (stream chunks), BatchImport
Bidirectional (N ↔ N)Real-time two-way communicationChat, collaborative editing, game state sync

gRPC in Production: Essential Patterns

PatternWhyImplementation
Deadlines / TimeoutsPrevents hanging requests; every gRPC call must have a deadlineSet deadline in metadata: 500ms for internal, 2s for external
Retries with BackoffHandles transient network failuresgRPC built-in retry policy config in service config
Interceptors (Middleware)Auth, logging, tracing, rate limitingUnaryServerInterceptor / StreamServerInterceptor
Load BalancingDistribute traffic across gRPC backendsClient-side (gRPC resolver) or proxy-based (Envoy, Linkerd)
Health CheckingK8s/load balancer needs to know service is healthyImplement grpc.health.v1.Health service
ReflectionEnable grpcurl and debugging toolsRegister reflection service in server

Bottom line: Use gRPC for internal service-to-service communication where performance matters — microservices, data-intensive backends, and real-time streaming. Use REST for public APIs, browser-facing endpoints, and when broad ecosystem compatibility (curl, Postman, web browsers) is needed. The two are not mutually exclusive — many teams use gRPC internally and expose REST externally via an API gateway (gRPC-gateway or Envoy). See also: tRPC vs GraphQL vs REST and API Design Patterns.