Advanced Guide

gRPC with Gofr

We have already seen how GoFr can help ease the development of HTTP servers, but there are cases where performance is primarily required sacrificing flexibility. In these types of scenarios gRPC protocol comes into picture. gRPC is an open-source RPC(Remote Procedure Call) framework initially developed by Google.

GoFr simplifies creating gRPC servers and enables efficient tracing across inter-service calls, leveraging its context for seamless data access and trace management in your handlers.

Prerequisites

1. Protocol Buffer Compiler (protoc) Installation:

  • Linux (using apt or apt-get):
  sudo apt install -y protobuf-compiler
  protoc --version # Ensure compiler version is 3+
  • macOS (using Homebrew):
  brew install protobuf
  protoc --version # Ensure compiler version is 3+

2. Go Plugins for Protocol Compiler:

a. Install protocol compiler plugins for Go:

  go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
  go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

b. Update PATH for protoc to locate the plugins:

  export PATH="$PATH:$(go env GOPATH)/bin"

Creating Protocol Buffers

For a detailed guide, refer to the official gRPC documentation's tutorial: Tutorial at official gRPC docs.

1. Define Your Service and RPC Methods:

Create a .proto file (e.g., customer.proto) to define your service and the RPC methods it provides:

// Indicates the protocol buffer version that is being used
syntax = "proto3";
// Indicates the go package where the generated file will be produced
option go_package = "path/to/your/proto/file";

service {serviceName}Service {
rpc {serviceMethod} ({serviceRequest}) returns ({serviceResponse}) {}
        }

2. Specify Request and Response Types:

Users must define the type of message being exchanged between server and client, for protocol buffer to serialize them when making a remote procedure call. Below is a generic representation for services' gRPC messages type.

message {serviceRequest} {
int64 id = 1;
        string name = 2;
// other fields that can be passed
        }

message {serviceResponse} {
int64 id = 1;
        string name = 2;
string address = 3;
// other customer related fields
        }

3. Generate Go Code:

Run the following command to generate Go code using the Go gRPC plugins:

  protoc \
	  --go_out=. \
	  --go_opt=paths=source_relative \
	  --go-grpc_out=. \
	  --go-grpc_opt=paths=source_relative \
	  {serviceName}.proto

This command generates two files, {serviceName}.pb.go and {serviceName}_grpc.pb.go, containing the necessary code for performing RPC calls.

Prerequisite: gofr-cli must be installed

To install the CLI -

  go install gofr.dev/cli/gofr@latest

Generating gRPC Server Handler Template using gofr wrap grpc server

1. Use the gofr wrap grpc server Command:

gofr wrap grpc server -proto=./path/your/proto/file
  gofr wrap grpc -proto=./path/your/proto/file

This command leverages the gofr-cli to generate a {serviceName}_server.go file (e.g., customer_server.go) containing a template for your gRPC server implementation, including context support, in the same directory as that of the specified proto file.

2. Modify the Generated Code:

  • Customize the {serviceName}GoFrServer struct with required dependencies and fields.
  • Implement the {serviceMethod} method to handle incoming requests, as required in this usecase:
    • Bind the request payload using ctx.Bind(&{serviceRequest}).
    • Process the request and generate a response.

Registering the gRPC Service with Gofr

1. Import Necessary Packages:

import (
	"path/to/your/generated-grpc-server/packageName"

	"gofr.dev/pkg/gofr"
)

2. Register the Service in your main.go:

func main() {
    app := gofr.New()

    packageName.Register{serviceName}ServerWithGofr(app, &packageName.{serviceName}GoFrServer{})

    app.Run()
}

Note: By default, gRPC server will run on port 9000, to customize the port users can set GRPC_PORT config in the .env

Generating tracing enabled gRPC Client using gofr wrap grpc client

1. Use the gofr wrap grpc client Command:

  gofr wrap grpc client -proto=./path/your/proto/file

This command leverages the gofr-cli to generate a {serviceName}_client.go file (e.g., customer_client.go). This file must not be modified.

2. Register the connection to your gRPC service inside your {serviceMethod} and make inter-service calls as follows :

// gRPC Handler with context support
func {serviceMethod}(ctx *gofr.Context) (*{serviceResponse}, error) {
// Create the gRPC client
srv, err := New{serviceName}GoFrClient("your-grpc-server-host")
if err != nil {
return nil, err
}

// Prepare the request
req := &{serviceRequest}{
// populate fields as necessary
}

// Call the gRPC method with tracing enabled
res, err := srv.{serviceMethod}(ctx, req)
if err != nil {
return nil, err
}

return res, nil
}
Check out the example of setting up a gRPC server in GoFr: Visit GitHub