11 Building Your First gRPC Server
Building an efficient communication system between services is essential in the era of microservices. gRPC has emerged as one of the modern solutions that is both powerful and scalable. With its HTTP/2-based protocol and Protobuf serialization, gRPC supports fast, lightweight, and robust communication across programming languages. In this article, I’ll guide you—from beginner to intermediate engineers—through building your very first gRPC server using Go.
This article is designed to be practical and step-by-step, complete with working code examples, request-response simulations, and flow diagrams. Let’s get started!
What Is gRPC?
gRPC is an open source RPC (Remote Procedure Call) framework from Google that runs on top of HTTP/2 and uses Protobuf for data serialization. With advantages such as streaming, authentication, and load balancing, gRPC has become a leading modern choice for Inter Process Communication (IPC).
High-Level gRPC Architecture
To make things clearer, here is a flow diagram showing how the gRPC client and server interact:
graph TD
A[Client] -- Request (Protobuf) --> B[gRPC Server]
B -- Response (Protobuf) --> A
- The client sends a request to the server using a “stub” (code generated from Protobuf).
- The server receives the request, processes it, and responds back to the client.
- All data is transmitted in Protobuf format over the HTTP/2 protocol.
Environment Prerequisites
Before you start coding, make sure you have installed:
- Go (version >= 1.16)
- protoc (the Protocol Buffer compiler) in your PATH
- The protoc-gen-go and protoc-gen-go-grpc plugins
- Your favorite text editor (VSCode, GoLand, etc.)
Install them with the following commands:
1go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
2go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest1. Defining the gRPC Service with Protobuf
Let’s start with a simple service. We’ll create a Greeter that has a single method: SayHello.
Create a file named helloworld.proto:
1syntax = "proto3";
2
3package helloworld;
4
5service Greeter {
6 rpc SayHello (HelloRequest) returns (HelloReply) {}
7}
8
9message HelloRequest {
10 string name = 1;
11}
12
13message HelloReply {
14 string message = 1;
15}Explanation:
service Greeterdefines the service.rpc SayHellois a remote method that accepts aHelloRequestand returns aHelloReply.
2. Generate Go Code from Protobuf
Run the generate command:
1protoc --go_out=. --go-grpc_out=. helloworld.protoThis will produce two files:
helloworld.pb.go(the Go message data types)helloworld_grpc.pb.go(the server and client interfaces)
3. Implementing the gRPC Server
Create a file server.go, then write the following implementation:
1package main
2
3import (
4 "context"
5 "log"
6 "net"
7
8 "google.golang.org/grpc"
9 pb "path/to/helloworld" // import according to the location of your pb.go file
10)
11
12type server struct {
13 pb.UnimplementedGreeterServer
14}
15
16// Implementation of the `SayHello` method
17func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
18 log.Printf("Received: %v", in.GetName())
19 return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
20}
21
22func main() {
23 lis, err := net.Listen("tcp", ":50051")
24 if err != nil {
25 log.Fatalf("failed to listen: %v", err)
26 }
27 grpcServer := grpc.NewServer()
28 pb.RegisterGreeterServer(grpcServer, &server{})
29 log.Printf("gRPC server started at :50051")
30 if err := grpcServer.Serve(lis); err != nil {
31 log.Fatalf("failed to serve: %v", err)
32 }
33}Key Points:
- The main function opens TCP port
:50051. pb.RegisterGreeterServerregisters the service.- The
SayHellomethod implementation greets the name from the client’s request.
Simulation: Example Request/Response
| Step | Description | Data Payload |
|---|---|---|
| Request | Client calls SayHello | { "name": "Andi" } |
| Response | Server returns the greeting | { "message": "Hello Andi" } |
4. Creating a Simple Client (Optional)
To make it more concrete, let’s build a simple client in Go:
1package main
2
3import (
4 "context"
5 "log"
6 "time"
7
8 "google.golang.org/grpc"
9 pb "path/to/helloworld"
10)
11
12func main() {
13 conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
14 if err != nil {
15 log.Fatalf("did not connect: %v", err)
16 }
17 defer conn.Close()
18 c := pb.NewGreeterClient(conn)
19
20 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
21 defer cancel()
22 r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "Andi"})
23 if err != nil {
24 log.Fatalf("could not greet: %v", err)
25 }
26 log.Printf("Greeting: %s", r.GetMessage())
27}Output on the Client:
1Greeting: Hello AndiOutput on the Server:
1gRPC server started at :50051
2Received: AndiRPC Flow Diagram
Here’s how the execution proceeds—from the client call all the way to the server response:
sequenceDiagram
participant C as Client
participant S as gRPC Server
C->>S: SayHello(name="Andi")
S-->>S: Proses SayHello()
S-->>C: HelloReply(message="Hello Andi")
A Few Best Practice Tips
| Tip | Notes |
|---|---|
| Error Handling | Provide detailed errors in the response (use gRPC status codes) |
| Logging | Log every call to make debugging and auditing easier |
| Versioning | Define new fields in a backward-compatible way in Protobuf |
| Testing | Write unit tests and integration tests using a gRPC server mock |
| Security | For production, use TLS (no longer grpc.WithInsecure()) |
Conclusion
Congrats! You’ve successfully built your very own gRPC server from scratch. We started by defining the service in Protobuf, generating the code, implementing the server, and finally setting up a client for testing.
gRPC is a powerful tool for building scalable, cross-platform systems. As a next step, you can experiment with streaming, authentication, and load balancing.
If you’d like to collaborate or share your experiences with gRPC in production, feel free to drop a comment below. Happy coding, engineer!
References: