Hi, readers this time I want to show you how to implement the classic Hello World example with gRPC, but with a small change: instead of sending a Name as a parameter, we are going to modify the protocol buffer definition and the generated code so the client sends a Nickname and the server replies back with it. We are going to use the same style of environment I used in my previous post about Container Runtimes, this time running everything inside a Killercoda Ubuntu playground and a golang container.
This blog post will focus on:
- Setting up a gRPC + Protoc development environment in Go.
- Modifying the official
grpc-goHello World example to use aNicknamefield. - Running the gRPC server and invoking it from a client process.
What you will learn
- Prepare the environment for gRPC and Protoc
- Implement a gRPC service
- Cloning and modifying the
grpc-goHello World example - Regenerating protobuf/gRPC Go code
- Running the gRPC server
- Cloning and modifying the
- Invoke a gRPC service from a client
Environment
For this exercise we are going to use the Ubuntu playground on Killercoda, which gives us a disposable Ubuntu machine with Docker already available. As a reference, I'm following the official gRPC Go quickstart guide and adapting it to this custom scenario.
Statement
Implement the gRPC Hello World example so it accepts a Nickname (instead of Name) as a parameter, and invoke it from a client process, similar to the example on the official gRPC site.
Prepare the environment for gRPC and Protoc
1. Inside the Killercoda Ubuntu playground, create a container using the official Go image:
docker run -it golang:1.25-bookworm /bin/bash
2. Prepare the gRPC environment by running the following commands inside the container:
apt-get update;apt-get install unzip;apt-get install nano vim -y
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
apt-get update;apt install protobuf-compiler -y
These commands install:
- unzip, nano, vim: basic utilities to unpack files and edit code.
- protoc-gen-go: the Go plugin for the protobuf compiler.
- protoc-gen-go-grpc: the Go plugin that generates gRPC service code.
-
protobuf-compiler (protoc): the compiler itself, used to generate Go code from
.protofiles.
Implement a gRPC service
1. Clone the grpc-go repository and remove the pre-generated protobuf files, since we are going to regenerate them after our changes:
git clone -b v1.77.0 --depth 1 https://github.com/grpc/grpc-go
cd grpc-go/examples/helloworld
rm helloworld/helloworld.pb*
2. Modify SayHello so it uses a Nickname parameter instead of Name. We need to touch three files:
-
helloworld/helloworld.proto: change theHelloRequestmessage to usenicknameinstead ofname. -
greeter_server/main.go: update theSayHellofunction to read and log theNicknamefield. -
greeter_client/main.go: update theconstandvardeclarations to usenickname(keep the finalGetMessagecall asGetMessage, since that is part of theHelloReplymessage, not the request).
helloworld/helloworld.proto should look like this:
syntax = "proto3";
option go_package = "google.golang.org/grpc/examples/helloworld/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the nickname.
message HelloRequest {
string nickname = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
In greeter_server/main.go, update the SayHello implementation to read in.GetNickname() and log it:
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Nickname Received: %s", in.GetNickname())
return &pb.HelloReply{Message: "Hello " + in.GetNickname()}, nil
}
In greeter_client/main.go, update the constants and flags to use nickname instead of name:
const (
address = "localhost:50051"
defaultNickname = "Linus"
)
var nickname = flag.String("nickname", defaultNickname, "Nickname to send")
And when calling SayHello, send the Nickname field but keep reading the reply through GetMessage, since the reply message was not modified:
r, err := c.SayHello(ctx, &pb.HelloRequest{Nickname: *nickname})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Nickname: %s", r.GetMessage())
3. Recompile the protobuf definition so the Go structs and gRPC stubs match our changes:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
helloworld/helloworld.proto
4. Run the server:
go run greeter_server/main.go
Wait until you see an output similar to this one:
2026/01/21 22:05:25 server listening at [::]:50051
Invoke a gRPC service from a client
5. Open a new terminal and access the same running container using docker exec. Once inside, run the client passing a nickname value:
cd grpc-go/examples/helloworld
go run greeter_client/main.go --nickname=linus2026
Expected results
Expected output on the server side:
Nickname Received: linus2026
Expected output on the client side (or similar):
2026/01/23 22:12:27 Nickname: linus2026
Conclusion about gRPC Hello World
This small exercise shows how easy it is to adapt the official gRPC Hello World example to fit a different use case, just by editing the .proto file, regenerating the stubs with protoc, and updating the server and client logic. Understanding this basic flow (proto definition → code generation → server implementation → client invocation) is the foundation for building more complex gRPC services in Go and other languages.
Thanks for reading! Last to say, see you in my next blog post.
Top comments (0)