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 1 $ 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 1 2 3 4 5 $ 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} 1 $ 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 1 2 3 4 5 6 7 8 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; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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 1 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) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 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) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 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 1 go run main . go

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

And you can view the output.

Hope this article helped you to build High-performance APIs in Go.

We offer Golang development services for building world-class enterprise apps. We have expertise in building most complex software solutions using Google’s Go language. Chat with us now and hire golang developers within 72 hours.