gRPC and Protocol Buffers
gRPC lets Go services call each other like local functions over the network. You define a contract once in a .proto file, generate typed client and server code with protoc , and get fast binary transport plus streaming over HTTP/2 — a strongly typed alternative to REST/JSON.
Learn gRPC and Protocol Buffers in our free Go course — an interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ The contract: a .proto file
Everything starts with the schema. A message is a typed struct; each field has a type, a name, and a unique field number (its tag on the wire). A service groups RPC methods. From this one file, protoc generates both the Go structs and the client/server stubs.
2️⃣ The server: grpc.NewServer() and register
Implement the generated GreeterServer interface, then wire it up in three steps: create the server with grpc.NewServer() , register your implementation with pb.RegisterGreeterServer , and call Serve on a listener. Embedding UnimplementedGreeterServer keeps you forward-compatible.
3️⃣ The client: grpc.Dial and call
The client dials the server to get a connection, wraps it in the generated client, and then calls methods as if they were local. Pass a context with a deadline so a hung server can't block you forever.
4️⃣ Streaming: more than one message
Unary is one-in, one-out — but gRPC also streams. Mark a method with the stream keyword in the .proto and the handler gets a stream to Send on (or Recv from) instead of a single return value.
🎯 Your Turn
Finish this unary handler. Fill in the two blanks marked ___ so the server greets the caller by name.
❌ Editing the generated .pb.go files — your changes vanish on regeneration.
✅ Change the .proto and re-run protoc ; never hand-edit generated code.
❌ Renumbering or reusing a field number — breaks already-deployed clients.
✅ Keep field numbers stable forever; add new fields with new numbers.
❌ Forgetting transport credentials on the client — the dial fails.
✅ For local dev pass insecure.NewCredentials() ; use TLS in production.
❌ Not embedding UnimplementedGreeterServer — code breaks when new methods are added.
✅ Embed it so unimplemented methods return a clear error instead of failing to compile.
Server streaming. Mark it in the .proto with returns (stream Reply) ; the handler calls stream.Send repeatedly.
It is the wire tag identifying that field in the binary encoding. It must stay constant across versions for compatibility.
Extend the contract: add AddRequest , AddReply , and an Add RPC on a Calculator service, then regenerate.
Practice quiz
What is a .proto file used for in gRPC?
- Defining messages and services as the contract between client and server
- Configuring the network firewall
- Logging RPC traffic
- Storing compiled binaries
Answer: Defining messages and services as the contract between client and server. The .proto file is the source of truth: it declares your messages and the service methods both sides agree on.
Which tool generates Go code from a .proto file?
- grpcurl
- go build
- protoc together with protoc-gen-go
- gofmt
Answer: protoc together with protoc-gen-go. protoc is the Protocol Buffer compiler; the protoc-gen-go and protoc-gen-go-grpc plugins emit the Go types and stubs.
How does Protocol Buffers send data on the wire?
- As CSV rows
- As a compact binary format
- As human-readable JSON
- As XML
Answer: As a compact binary format. Protobuf serializes to a compact, efficient binary representation, which is smaller and faster to parse than JSON text.
What does the field number after each field in a message mean (e.g. name = 1)?
- A default value
- The maximum length
- An array index that must start at zero
- A unique tag identifying that field on the wire
Answer: A unique tag identifying that field on the wire. Field numbers are the stable wire identifiers; they must stay constant across versions so old and new code stay compatible.
Which function creates a new gRPC server in Go?
- grpc.NewServer()
- grpc.Open()
- grpc.Listen()
- grpc.Serve()
Answer: grpc.NewServer(). grpc.NewServer() returns a *grpc.Server; you then register your service and call Serve on a net.Listener.
How does a client establish a connection to a gRPC server?
- grpc.Accept()
- grpc.Dial (or grpc.NewClient) to get a *grpc.ClientConn
- http.Get on the service URL
- net.Listen on the port
Answer: grpc.Dial (or grpc.NewClient) to get a *grpc.ClientConn. The client dials the target address to obtain a *grpc.ClientConn, which it passes to the generated client constructor.
What is a UNARY RPC?
- A call that never returns
- A call with no request message
- A stream of requests
- A single request that returns a single response
Answer: A single request that returns a single response. Unary is the simplest pattern: one request in, one response out, like a normal function call over the network.
Which streaming pattern lets BOTH sides send a stream of messages at once?
- Client streaming
- Unary
- Bidirectional streaming
- Server streaming
Answer: Bidirectional streaming. Bidirectional streaming opens two independent streams so client and server can read and write concurrently.
After registering your implementation, which call starts handling requests?
- server.Run()
- server.Serve(listener)
- server.Start()
- server.Handle()
Answer: server.Serve(listener). grpcServer.Serve(lis) blocks and serves incoming connections on the provided net.Listener until it stops.
A key advantage of gRPC over plain REST/JSON is that it...
- generates strongly typed client and server code from one contract
- requires no schema at all
- only works in the browser
- cannot do streaming
Answer: generates strongly typed client and server code from one contract. From a single .proto contract gRPC generates typed stubs for both sides, plus efficient binary transport and built-in streaming over HTTP/2.