back

grpc

nicholas chen · january 11, 2026 · 9 min read

gRPC

in modern software architecture, especially with microservices, the way services communicate is crucial. while rest has been the standard for a long time, grpc has emerged as a powerful alternative for many use cases. in this post, i'll explain what grpc is, how it works, and why you might want to use it.

what is gRPC?

grpc (google remote procedure call) is an open-source remote procedure call framework initially developed by google. it allows a client application to directly call a method on a server application on a different machine as if it were a local object, making it easier to create distributed applications and services. unlike rest, which is resource-oriented, grpc is action-oriented.

how it works

this is typically done using protocol buffers (protobuf) in a .proto file. at a high level, gRPC allows you to define a service, specifying the methods that can be called remotely with their parameters and return types. the server implements this interface and runs a gRPC server to handle incoming calls. the client has a stub (referred to as just a client in some languages) that provides the same methods as the server.

gRPC Architecture

gRPC architecture: client stub and server stub interaction

by default, gRPC uses protocol buffers (protobuf) as its interface definition language (IDL) and as its underlying message interchange format. in REST, you often use JSON. with protobuf, you define the structure of your data once in a .proto file. then, you use the protoc compiler to generate data access classes in your preferred programming language. this binary format is much lighter and faster to serialize/deserialize than JSON.

syntax = "proto3";

package greeting;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

example .proto service definition

using the API

starting from a .proto file, gRPC compiler plugins generate client- and server-side code. the server implements the service methods and runs a gRPC server to handle calls. the client uses a local stub object that implements the same methods, wrapping parameters in protocol buffer messages and sending requests to the server.

gRPC APIs support both synchronous (blocking) and asynchronous (non-blocking) calls, useful for different network operation scenarios.

service method types

gRPC lets you define four kinds of service methods:

gRPC streaming types

gRPC service method types

  • unary RPCs: single request, single response.
    rpc GetUser(UserRequest) returns (UserResponse);
  • server streaming RPCs: client sends request, receives stream of messages.
    rpc ListItems(ListRequest) returns (stream ItemResponse);
  • client streaming RPCs: client sends stream of messages, receives single response.
    rpc UploadData(stream DataChunk) returns (UploadResponse);
  • bidirectional streaming RPCs: both sides send streams of messages independently.
    rpc Chat(stream MessageRequest) returns (stream MessageResponse);

deadlines and timeouts

clients can specify how long to wait for an RPC before it's terminated with DEADLINE_EXCEEDED. servers can query timeout status and remaining time.

RPC termination and cancellation

client and server make independent determinations of call success, so their conclusions may not match. either side can cancel an RPC at any time, which terminates it immediately. changes made before cancellation are not rolled back.

metadata

metadata is key-value pairs containing information about an RPC call (e.g., authentication). keys are case-insensitive ASCII strings, must not start with grpc- (reserved), and binary-valued keys end in -bin.

channels

a gRPC channel provides a connection to a server on a specified host and port, used when creating client stubs. clients can configure channel arguments to modify gRPC behavior (e.g., message compression). channels have state (connected, idle).

error handling

gRPC uses a standardized error model with status codes. common status codes include OK, INVALID_ARGUMENT, NOT_FOUND, UNAVAILABLE, and DEADLINE_EXCEEDED. errors include both a status code and an optional error message, providing consistent error handling across languages.

security and authentication

gRPC supports TLS (transport layer security) for encrypted communication between client and server. mTLS (mutual TLS) provides mutual authentication where both sides verify each other's certificates. authentication credentials can also be passed via metadata, allowing for various authentication mechanisms including OAuth2, JWT tokens, and API keys.

why is it good?

gRPC leverages HTTP/2's multiplexing, header compression, and binary framing for better efficiency than REST/HTTP/1.1. The .proto contract enables automatic code generation in multiple languages, ensuring type safety. Native streaming support (server, client, or bidirectional) makes it ideal for real-time apps, large file transfers, and long-lived connections.

HTTP/1.1 vs HTTP/2

gRPC uses HTTP/2 as its transport protocol, which provides significant improvements over HTTP/1.1. The following table compares the key features of both protocols.

featureHTTP/1.1HTTP/2
multiplexingno (one request per connection)yes (multiple requests over single connection)
header compressionnoyes (HPACK)
framingtext-basedbinary framing
server pushnoyes
request prioritizationnoyes
efficiencyhigher latency, more bandwidthlower latency, reduced bandwidth
HTTP 1.1 vs HTTP/2

http/1.1 vs http/2 multiplexing

RPC vs REST

RPC focuses on actions (verbs) like "getUser", while REST focuses on resources (nouns) like "User". this makes gRPC feel more like calling a local function, simplifying distributed development. REST is great for public APIs where human readability (JSON) and browser support are important. it's flexible and widely understood. gRPC, on the other hand, excels in internal microservices communication where low latency and high throughput are critical. it's also strongly typed, which helps in maintaining large systems.

featuregRPCREST
transportHTTP/2HTTP/1.1
data formatprotocol buffers (binary)JSON (text)
streamingnative supportlimited (SSE, WebSocket)
code generationautomatic from .protomanual
type safetyenforced by contractruntime validation
performancehigh (multiplexing, compression)lower latency, more bandwidth
browser supportlimited (gRPC-Web required)native

protocol buffers vs JSON

// Protocol Buffers (.proto)
message User {
  string name = 1;
  int32 age = 2;
  string email = 3;
}

// Serialized (binary, compact)

protocol buffers definition and serialized format (binary)

// JSON (text, human-readable)
{
  "name": "John Doe",
  "age": 30,
  "email": "john@example.com"
}

JSON format (human-readable text)

gRPC with go

gRPC and go are super compatible with each other. since both originated from google, gRPC support in go is first-class. the go ecosystem embraces gRPC for microservices due to go's concurrency model (goroutines) which handles HTTP/2 multiplexing efficiently.

while go has excellent gRPC support, gRPC itself supports many languages including java, python, c++, node.js, rust, ruby, php, and more. code generation ensures consistent behavior across all language implementations.

to use gRPC with go, define your service in a .proto file and use the protoc compiler with protoc-gen-go and protoc-gen-go-grpc plugins. this generates message structs and service interfaces. on the server, implement the generated interface and register it with grpc.NewServer(). on the client, use grpc.Dial() to connect and create a client stub. the generated code is idiomatic go.

gRPC with Go

the go programming language

// Server implementation
type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + req.Name}, nil
}

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    s.Serve(lis)
}

example gRPC server implementation in go

// Client implementation
func main() {
    conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
    defer conn.Close()
    
    c := pb.NewGreeterClient(conn)
    ctx := context.Background()
    r, _ := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})
    
    fmt.Println(r.Message)
}

example gRPC client implementation in go

when to use gRPC

gRPC is ideal for:

  • microservices architectures where services need efficient, low-latency communication
  • real-time systems like chat applications, gaming backends, and live data feeds
  • mobile APIs that benefit from gRPC's binary format, reducing bandwidth usage and battery consumption
  • streaming use cases such as file transfers, log aggregation, and real-time analytics

gRPC is widely used by:

  • companies like google, netflix, and square for internal microservices communication
  • kubernetes for its API
  • cloudflare for edge computing

references

/