Unary gRPC with Golang – Part 2

GRPC Go (2)

This is the final part of the gRPC tutorial. If you haven’t checked the rest out, here’s the link:

We have already covered the basics, and code generation, and testing. Now we’ll code our main server and client.

Main Server and Client

Create two separate folders – server and client. Each of these will have their own main.go files.

Also, let’s create our Makefile:

	protoc --proto_path=proto proto/*.proto --go_out=plugins=grpc:pb --grpc-gateway_out=:pb --openapiv2_out=:swagger

	rm pb/*.go 

	go run ~/server/main.go -port 50051

	go run ~/server/main.go -port 50052

	go run ~/server/main.go -port 50051 -tls

	go run ~/server/main.go -port 50052 -tls

	go run ~/server/main.go -port 8080

	go run ~/server/main.go -port 8080 -tls

	go run ~/server/main.go -port 8081 -type rest -endpoint

	go run ~/client/main.go -address

	go run ~/client/main.go -address -tls

	go test -cover -race ./...

	cd cert; ./gen.sh; cd ..

.PHONY: gen clean server client test cert 

Open the server/main.go file:

func main() {
    port := flag.Int("port", 0, "server port")
    log.Printf("start server on port %d", *port)

    laptopStore := service.NewInMemoryLaptopStore()
    laptopServer := service.NewLaptopServer(laptopStore)
    pb.RegisterLaptopServiceServer(grpcServer, laptopServer)

    address := fmt.Sprintf("", *port)
    listener, err := net.Listen("tcp", address)
    if err != nil {
        log.Fatal("cannot start server: ", err)

    err = grpcServer.Serve(listener)
    if err != nil {
        log.Fatal("cannot start server: ", err)

We build an address string with the port that we’ve had before, then listen to this server address for TCP connexions. We need a port for the server, so I use the flag.Int() function to get it from command line arguments.

Finally, grpcServer. Serve() is called to start the server. Just write a fatal log and exit if any error occurs. And that’s the code for a server.

Next, we do the client side:

func main() {
    serverAddress := flag.String("address", "", "the server address")
    log.Printf("dial server %s", *serverAddress)

    conn, err := grpc.Dial(*serverAddress, grpc.WithInsecure())
    if err != nil {
        log.Fatal("cannot dial server: ", err)

    laptopClient := pb.NewLaptopServiceClient(conn)

    laptop := sample.NewLaptop()
    req := &pb.CreateLaptopRequest{
        Laptop: laptop,

    // set timeout
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    res, err := laptopClient.CreateLaptop(ctx, req)
    if err != nil {
        st, ok := status.FromError(err)
        if ok && st.Code() == codes.AlreadyExists {
            // not a big deal
            log.Print("laptop already exists")
        } else {
            log.Fatal("cannot create laptop: ", err)

    log.Printf("created laptop with id: %s", res.Id)

We call grpc.Dial() function with the input address, and create a connection.

If an error occurs, we write a fatal log and exit. Else, we create a new laptop client object with the connection.

Then we generate a new laptop, make a new request object, and just call laptopClient.Createlaptop() function with the request and a context. Here we use context.WithTimeout() to set the timeout of this request to be 5 seconds.

If the error is not nil, we convert it into a status object. If the status code is ‘AlreadyExists‘ then we write a normal log, else a fatal log.

If everything is good, we simply write a log saying the laptop is created with this ID. And that’s it for the client.

And we’re at the end of our tutorial. If we run this using go run ~/server/main.go -port 8080, we get:

Grpc Saved Entry From Client
Grpc Saved Entry From Client

If we cancel the request using Ctrl + C at the client side, we get a canceled message:

Cancelled Request Grpc
Cancelled Request Grpc