Building High Performance APIs In Go Using gRPC

As web developers we all the know the importance of APIs in web applications, they are considered as the cornerstones of the applications. APIs directs or defines how one application can talk to another application. As the technology advances, the current applications we build will definitely need a High-performance APIs. So here in this article, I’m going to explain how we can build a high-performance APIs in GO using gRPC and protocol buffers. Before going to the actual procedure lets understand gRPC and protocol buffers.

What is gRPC?

RPC stands for remote procedure call. gRPC is an RPC framework that runs anywhere. It allows the transparent communication between client and server applications.

What are protocol buffers?

Protocol buffers are flexible, efficient, and automated. they serializes structured data – think XML, they are very simple yet fast. After defining our data, by using its compiler called protoc we can generate source code in any programming language. 

Before you begin,

Install gRPC

Use the following command to install gRPC.

$ go get google.golang.org/grpc

Install Protocol Buffers v3

Install the protoc compiler that is used to generate gRPC service code. The simplest way to do this

$ curl -OL https://github.com/google/protobuf/releases/download/v3.0.0-beta-2/protoc-3.0.0-beta-2-linux-x86_64.zip

$ unzip protoc-3.0.0-beta-2-linux-x86_64.zip -d protoc3

$ sudo mv protoc3/protoc /bin/protoc

Next, Install the protoc plugin for Go

$ go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

Building a sample API in Go:

With the use of gRPC and protocol buffers, here we are going to build an API for example. we use the following directory structure in this application.

grpc
├── client
   └── main.go
├── person
   └── person.proto

   └── person.pb.go

└── server
   └── main.go

Message Types and Service Definition :

Here is the source of person.proto file in person directory. This file is protocol buffer file.


syntax = "proto3";

package person;

// The Person service definition.

service Person {   

 // Get all Persons with filter - A server-to-client streaming RPC.

 rpc GetPersons(PersonFilter) returns (stream PersonRequest) {}

 // Create a new Person - A simple RPC

 rpc CreatePerson (PersonRequest) returns (PersonResponse) {}

}

// Request message for creating a new person

message PersonRequest {

 int32 id = 1;  // Unique ID number for a Person.

 string name = 2;

 string email = 3;

 string phone= 4;

 

 message Address {

   string street = 1;

   string city = 2;

   string state = 3;

   string zip = 4;

   bool isShippingAddress = 5;

 }

 repeated Address addresses = 5;

}

message PersonResponse {

 int32 id = 1;

 bool success = 2;

}

message PersonFilter {    

 string keyword = 1;

}

We declare the name “person”. When you generate Go source code from proto file, it will add Go package name as “person”.

Generating Go Code for Client and Server:

After defining proto file, we have to generate source code for the gRPC client and server interfaces. We use protoc with gRPC Go plugin for code generation. Now run this protoc from the root directory of the application.

protoc -I person/ person/person.proto --go_out=plugins=grpc:person

Now person.pb.go file will be generated in person directory. This gives the code in order to create the server and make client calls.

Creating the gRPC Server:

package main

import (

 "fmt"

 "net"

 "strings"

 "google.golang.org/grpc"

 "golang.org/x/net/context"

 prsn "github.com/agiratech/golang-rpc/person"

)

const (

 port = ":3333"

)

// Person is used to implement prsn.PersonServer.

type Person struct {

 savedPersons []*prsn.PersonRequest

}

// CreatePerson creates a new Person

func (p *Person) CreatePerson(c context.Context, input *prsn.PersonRequest) (*prsn.PersonResponse, error) {

 p.savedPersons = append(p.savedPersons, input)

 return &prsn.PersonResponse{Id: input.Id, Success: true}, nil

}

// GetPersons returns all persons using filter

func (p *Person) GetPersons(fltr *prsn.PersonFilter, stream prsn.Person_GetPersonsServer) error {

 for _, person := range p.savedPersons {

   if fltr.Keyword != "" {

     if !strings.Contains(person.Name, fltr.Keyword) {

       continue

     }

   }

   err := stream.Send(person)

   if err != nil {

     return err

   }

 }

 return nil

}

func main() {

 lis, err := net.Listen("tcp", port)

 if err != nil {

   fmt.Println("failed to listen: ", err)

   return

 }

 // Creates a new gRPC Person

 s := grpc.NewServer()

 prsn.RegisterPersonServer(s, &Person{})

 s.Serve(lis)

}

Creating the gRPC Client:

Now lets create gRPC Client using following code.

package main

import (

 "io"

 "fmt"

 "google.golang.org/grpc"

 "golang.org/x/net/context"

 prsn "github.com/agiratech/golang-rpc/person"

)

const (

 address = "localhost:3333"

)

// createPerson calls the RPC method CreatePerson of PersonServer

func createPerson(client prsn.PersonClient, person *prsn.PersonRequest) {

 resp, err := client.CreatePerson(context.Background(), person)

 if err != nil {

   fmt.Println("Could not create Person: ", err)

   return

 }

 if resp.Success {

   fmt.Println("A new Person has been added with id: ", resp.Id)

 }

}

// getPersons calls the RPC method GetPersons of PersonServer

func getPersons(client prsn.PersonClient, filter *prsn.PersonFilter) {

 // calling the streaming API

 stream, err := client.GetPersons(context.Background(), filter)

 if err != nil {

   fmt.Println("Error on get persons: ", err)

   return

 }

 for {

   person, err := stream.Recv()

   if err == io.EOF {

     break

   }

   if err != nil {

     fmt.Printf("%v.GetPersons(_) = _, %v", client, err)

   }

   fmt.Println("Person: ", person)

 }

}

func main() {

 // Set up a connection to the gRPC server.

 conn, err := grpc.Dial(address, grpc.WithInsecure())

 if err != nil {

   fmt.Println("did not connect: ", err)

   return

 }

 defer conn.Close()

 // Creates a new PersonClient

 client := prsn.NewPersonClient(conn)

 person := &prsn.PersonRequest{

   Id:    1001,

   Name:  "Reddy",

   Email: "reddy@xyz.com",

   Phone: "9898982929",

   Addresses: []*prsn.PersonRequest_Address{

     &prsn.PersonRequest_Address{

       Street:            "Tripilcane",

       City:              "Chennai",

       State:             "TN",

       Zip:               "600002",

       IsShippingAddress: false,

     },

     &prsn.PersonRequest_Address{

       Street:            "Balaji colony",

       City:              "Tirupati",

       State:             "AP",

       Zip:               "517501",

       IsShippingAddress: true,

     },

   },

 }

 // Create a new person

 createPerson(client, person)

 person = &prsn.PersonRequest{

   Id:    1002,

   Name:  "Raj",

   Email: "raj@xyz.com",

   Phone: "5000510001",

   Addresses: []*prsn.PersonRequest_Address{

     &prsn.PersonRequest_Address{

       Street:            "Marathahalli",

       City:              "Bangalore",

       State:             "KS",

       Zip:               "560037",

       IsShippingAddress: true,

     },

   },

 }

 // Create a new person

 createPerson(client, person)

 // Filter with an empty Keyword

 filter := &prsn.PersonFilter{Keyword: ""}

 getPersons(client, filter)

}

For running the gRPC server , we have to run main.go from the directory – server

go run main.go

And for testing RPC method , we have to run main.go from the directory- client


go run main.go

And you can view the output.

 

Hope this article helped you to build High-performance APIs in Go. Follow Agira Technologies to keep updated with the latest advancements in web development. We have developed a remarkable number of projects using the latest technologies and have expertise in web development.