12 Building Your First gRPC Client
In the modern software development ecosystem, inter-service communication is the backbone of microservices architecture. One of the most popular and efficient RPC (Remote Procedure Call) protocols, developed by Google, is gRPC. This protocol has become the de facto standard for communication between backend services in many organizations. However, understanding and implementing your first gRPC client can be a challenge in itself if you aren’t guided systematically.
In this article, I’ll walk you through the process of building your first gRPC client: from generating the stub code, to building the client application code, all the way to performing real communication with a server. I’ll also include code examples, simulations, comparison tables, and an end-to-end flow diagram.
What Is gRPC?
Before we move into the practical stage, let’s briefly review what gRPC is and why it’s so popular.
gRPC is a modern RPC framework that uses Protobuf (Protocol Buffers) as its data serialization format. Its advantages include:
- High performance (HTTP/2)
- Clear API definitions via
.protofiles - Cross-platform and cross-language support
- Mature documentation and a robust tooling ecosystem
Initial Preparation
Before building the client, make sure you’ve completed the following:
- A gRPC server is already running (or you at least have its .proto file)
protoc— the Protobuf compiler — is installed- The gRPC plugin for your programming language of choice is available (in this article: Go, which is popular and easy to follow)
The basic directory structure of the project will look like this:
1grpc-demo/
2├── proto/
3│ └── hello.proto
4├── server/
5│ └── main.go
6└── client/
7 └── main.goDefining the API with .proto
Let’s say we have a simple API: the client asks the server to return a greeting.
Create the following proto/hello.proto file:
1syntax = "proto3";
2
3package hello;
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}Generating the Stub Code
Run the following command from the project root to generate the gRPC code for Go:
1protoc --go_out=. --go-grpc_out=. proto/hello.protoThis will generate Go files in the proto/ folder. They contain both the client and server interfaces.
Steps to Build a gRPC Client
1. The Communication Flow Concept
How does a gRPC client work? Let’s take a look at the following diagram:
sequenceDiagram
participant Client
participant Stub
participant Server
Client->>Stub: Memanggil stub local (SayHello)
Stub->>Server: Membuat dan mengirim request (HelloRequest)
Server->>Stub: Mengembalikan response (HelloReply)
Stub->>Client: Mengembalikan nilai (HelloReply)
The client stub acts as a “local proxy,” so calling a remote function is as easy as calling a local method.
2. Writing the Client Code
Open client/main.go and write the following code:
1package main
2
3import (
4 "context"
5 "log"
6 "os"
7 "time"
8
9 "google.golang.org/grpc"
10 pb "grpc-demo/proto" // Import the generated .proto code
11)
12
13const (
14 address = "localhost:50051"
15 defaultName = "dunia"
16)
17
18func main() {
19 conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
20 if err != nil {
21 log.Fatalf("gagal koneksi: %v", err)
22 }
23 defer conn.Close()
24 c := pb.NewGreeterClient(conn)
25
26 name := defaultName
27 if len(os.Args) > 1 {
28 name = os.Args[1]
29 }
30
31 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
32 defer cancel()
33
34 r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
35 if err != nil {
36 log.Fatalf("gagal greeting: %v", err)
37 }
38 log.Printf("Salam: %s", r.GetMessage())
39}A breakdown of the steps:
- Create the connection:
grpc.Dial - Create the client object:
NewGreeterClient - Set the timeout and context
- Call the RPC:
SayHello - Receive the response from the server
3. Simulating the Client-Server Interaction
Let’s simulate the code path involved:
| Step | Object | Function | Info/Data |
|---|---|---|---|
| 1. Connection | grpc.Conn | Dial(address) | Opens an HTTP/2 channel to Srv |
| 2. Stub | GreeterClient | NewGreeterClient() | Instantiates the client playground |
| 3. Request | Client | SayHello(ctx, req) | Sends HelloRequest{Name} |
| 4. Response | Server | SayHello() | Returns HelloReply{Message} |
| 5. Output | Client | Receives the response | Print message / handle error |
Common Challenges When Building a gRPC Client
1. Protobuf Path Errors
Make sure the import path is correct. For Go, you typically use:
1pb "grpc-demo/proto"2. TLS vs Insecure
During development, you can use grpc.WithInsecure(), but in production you MUST use TLS for security.
3. Timeout & Context
The context lets you set a timeout and cancel an RPC activity if the response takes too long.
Example Output
If your server is running and you execute:
1go run client/main.go DuniaThen the output will look like this:
12024/07/01 Salam: Halo DuniaComparing the gRPC Client with a REST Client
Here’s a concise table summarizing the advantages of a gRPC client over REST:
| Feature | gRPC | REST |
|---|---|---|
| Data Format | Protobuf (binary, compact) | JSON (plain) |
| Performance | High (HTTP/2) | Tends to be lower |
| Type Safety | Yes | No (JSON) |
| Streaming | Yes (bi-directional) | Limited |
| Auto Codegen | Strong (protoc) | Manual |
| Security | TLS/Mutual TLS | Possible |
Client Debugging Tips
- Use
grpclog.SetLoggerV2for verbose debugging - Check the port and service registry if you encounter a connection refused error
- A mismatch in the Protobuf definitions between client and server will cause an Unimplemented error
Starting the Server (For Testing)
Make sure a server is running on localhost:50051. Here’s an example of a simple service:
1package main
2
3import (
4 "context"
5 "log"
6 "net"
7
8 "google.golang.org/grpc"
9 pb "grpc-demo/proto"
10)
11
12type server struct {
13 pb.UnimplementedGreeterServer
14}
15
16func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
17 return &pb.HelloReply{Message: "Halo " + in.GetName()}, nil
18}
19
20func main() {
21 lis, err := net.Listen("tcp", ":50051")
22 if err != nil {
23 log.Fatalf("failed to listen: %v", err)
24 }
25 grpcServer := grpc.NewServer()
26 pb.RegisterGreeterServer(grpcServer, &server{})
27 log.Println("Server berjalan di :50051...")
28 grpcServer.Serve(lis)
29}Conclusion
Building your first gRPC client is an important milestone for any modern engineer, especially those working with microservices. With its efficient protocol, built-in type safety, and automated code generation tooling, building a gRPC client is almost as easy as using a regular HTTP client—but with performance, security, and scalability far above average. Once you understand the role of the .proto file, the stub, and the RPC execution order, you’re ready to explore advanced features such as streaming, error handling, and interceptors in gRPC.
Good luck building your first gRPC client — and welcome to the world of professional distributed systems!