Talk to other apps with gRPC


I'm going to write a customer service that is running by using gRPC and then create a client to talk with the service

First, create a Go module and write a proto file

mkdir grpc-api
cd grpc-api
mkdir protos
touch protos/customer.proto
syntax = "proto3";
package customerpb;
option go_package="server/customerpb";

message CustomerInfo {
  string customer_id = 1;
  string customer_name = 2;
}

message GetCustomersRequest {}

message GetCustomersResponse {
  repeated CustomerInfo customers = 1;
}

message GetCustomerByIdRequest {
  string customer_id = 1;
}

message GetCustomerByIdResponse {
  CustomerInfo customer = 1;
}

service Customer {
  rpc GetCustomers(GetCustomersRequest) returns (GetCustomersResponse) {};
  rpc GetCustomerById(GetCustomerByIdRequest) returns (GetCustomerByIdResponse) {};
}

Then create the client module and the customer service module

mkdir client && cd client
go mod init grpc-api/client
cd ..
mkdir customer && cd customer
go mod init grpc-api/customer
cd ..
go work init ./client
go work use ./customer

Now install protoc to work with .proto file

sudo apt install -y protobuf-compiler

Install Go plugin for the protoc and generate Go files for gRPC server/client

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
export PATH="$PATH:$(go env GOPATH)/bin"

And generate the Go gRPC files

protoc --go_out=./client --go-grpc_out=./client protos/customer.proto
protoc --go_out=./customer --go-grpc_out=./customer protos/customer.proto

Then implement the customer service

touch customer/server/server.go
package server

import (
	"context"
	"grpc-api/customer/server/customerpb"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type Customer struct {
	ID   string
	Name string
}

var _customers = []Customer{{ID: "A310", Name: "There"}, {ID: "K423", Name: "Kian"}}

type CustomerServer struct {
	customerpb.UnimplementedCustomerServer
}

func (CustomerServer) GetCustomers(ctx context.Context, in *customerpb.GetCustomersRequest) (*customerpb.GetCustomersResponse, error) {
	res := &customerpb.GetCustomersResponse{}
	for _, customer := range _customers {
		res.Customers = append(res.Customers, &customerpb.CustomerInfo{
			CustomerId:   customer.ID,
			CustomerName: customer.Name,
		})
	}
	return res, nil
}
func (CustomerServer) GetCustomerById(ctx context.Context, in *customerpb.GetCustomerByIdRequest) (*customerpb.GetCustomerByIdResponse, error) {
	for _, customer := range _customers {
		if customer.ID == in.CustomerId {
			return &customerpb.GetCustomerByIdResponse{
				Customer: &customerpb.CustomerInfo{
					CustomerId:   customer.ID,
					CustomerName: customer.Name,
				},
			}, nil
		}
	}
	return nil, status.Errorf(codes.NotFound, "the customer does not exist")
}

Next, write the main.go file for the server

touch customer/main.go
package main

import (
	"context"
	"grpc-api/customer/server"
	"grpc-api/customer/server/customerpb"
	"log"
	"net"
	"os/signal"
	"syscall"

	"google.golang.org/grpc"
)

func main() {
	lis, err := net.Listen("tcp", ":9090")
	if err != nil {
		log.Fatalln(err)
	}
	srv := grpc.NewServer()
	customerpb.RegisterCustomerServer(srv, &server.CustomerServer{})

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	go func() {
		<-ctx.Done()
		stop()
		log.Println("Server is exiting")
		srv.Stop()
	}()

	log.Println("Server is running...")
	if err := srv.Serve(lis); err != nil && err != grpc.ErrServerStopped {
		log.Fatalf("Error while serving: %v", err)
	}
	log.Println("Server is closed")
}

Then write the client

touch client/main.go
package main

import (
	"context"
	"grpc-api/client/server/customerpb"
	"log"

	"google.golang.org/grpc"
)

func main() {
	conn, err := grpc.Dial(":9090", grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}
	res, err := customerpb.NewCustomerClient(conn).GetCustomers(
		context.Background(),
		&customerpb.GetCustomersRequest{},
	)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(res.Customers)

	if len(res.Customers) > 0 {
		res, err := customerpb.NewCustomerClient(conn).GetCustomerById(
			context.Background(),
			&customerpb.GetCustomerByIdRequest{
				CustomerId: res.Customers[0].CustomerId,
			},
		)
		if err != nil {
			log.Fatal(err)
		}
		log.Println(res.Customer.CustomerName)
	}
}

Ok~ now you can start the apps

For the server, run this

go run grpc-api/customer

And for the client, use this

go run grpc-api/client

Finally, gRPC services are working

sources