Golang. CRUD with gRPC.

Yuri Fenyuk
5 min readMay 9, 2023

--

The current chapter is the continuation of Golang related series started with the previous ones:

The input for the current chapter is here. The project has REST API endpoint implementation to support usual CRUD operations. It also utilizes Protobuf to persist storage into the file on the disk to keep it between server runs. This chapter goes one step further using Protobuf schema to describe gRPC service and use existing message to pass between server and remote client.

gRPC is open sources framework for interprocess and intercomputer communications. In its simplest and most straightforward usage, gRPC uses Protobuf serialized (i.e. binary format) messages and TCP as transport. In the previous chapter, I used Protobuf only as entity persistence format. As a starting point, the Protobuf schema defining message only already exists:

old Protobuf schema

The schema needs to be extended with Protobuf service which results in RPC generated code:

updated Protobuf schema

#5..#6: importing useful here-and-there general purpose messages.

#18..#21: RPC operation can only have one request parameter according to specification. For Update part of CRUD, two parameters need to be passed (ID defined at #19 and Brand defined at #20) so the workaround is to create composite message type.

#23..#29: PRC service definition. This service consists of 5 self-explanatory operations(#24 up to #28). The interesting one is GetList(). As it should return a set of Brands, the return type is marked with stream operator, which is at runtime yielding items one after the other. (The alternative approach is to define response message type, which has repeated Brands field close to what is at #15).

With that schema it is possible to generate Go files for Protobuf messages and gRPC service with the following command:

— go_out: as before telling proto-compiler that the destination folder for Protobuf messages is protobuf.

— go-grpc_out: similar but for gRPC client and server.

— go-grpc_opt: instructs gRPC Go plugin to skip unimplemented feature.

two generated Go files

brand.pb.go: generated Go file with Protobuf messages.

brand_grpc.pb.go: generate Go file with gRPC client and server.

Files can be found here. Particular interest draws file brand_grpc.pb.go as it has the signatures of all RPC operation needs to be implemented on server side:

Useful method definitions

Luckily, it is possible to reuse existing Brand Repository type in gRPC server implementation, to mostly pass data between layers. Moreover, same Repository is used in REST API server created before and it stores data in same file (BTW, serialized with help of Protobuf messages defined in brand.pb.go).

The implemented gRPC server is in protobuf/server/server.go file:

CRUDServiceServer composes Brand Repository. It function signatures repeat generated RPC interface and pass input to Repository and back. The implementation is really straightforward.

Next step is to write function to run gRPC server. In the current chapter, I will have to servers running: old REST API on port 6000 and new gRPC server. It is a somewhat too much and only OK for demo purposes. Luckily, the Brand Repository is the same for both servers, even one instance. Thus, the will be reflection of what changed via REST API with gRPC and vice versa.

Here is the new function in main.go:

running gRPC server

#2: create TCP server on port 6002.
#7: create new instance of gRPC server from the standard gRPC package.

#8: link to standard server custom part which knows how to handle CRUD service, defined in Protobuf schema.

#11: put gRPC server online.

Function StartRPCServer(…) should be called as goroutine to allow both servers to operate. Final code of main.go is here.

Both servers are running

As I can continue to use brand.rest and VC plugin to test REST API, for gRPC part the Postman can be utilized. Here is blog post on how to set it up. I created the new Postman collection (CRUD) and imported brand.proto within the project. After Postman knows operations and signatures, they become visible in combobox:

List of RPC operations

Knowing that GetList() has no parameters, it is easy to execute it:

gRPC GetList operation

This operation is particularly interesting. Remember how this operation is defined in Protobuf schema:

It returns stream of Brands. Thus, the log shows (bottom up) that the initial response was empty and only after Brands were reaching Postman one after another.

Same results for GetList(…) counterparty in REST API:

REST API GetList operation

Let’s return to Postman to execute GetOne(id) operation:

gRPC GetOne operation

Passing non-existing ID to this operation results with failure status code and even error message in red box:

Passing the wrong id to gRPC GetOne

The next test is to update the existing Brand:

gRPC Update operation

And again select all Brands to see updated also:

gRPC GetList operation

Latest data is also in REST API endpoint:

REST API GetOne operation

The final version of the project is here.

gRPC is becoming the major protocol of inter-service communication within microservice architecture. And there multiple reasons ‘why’. My test project is somewhat redundant because it runs 2 servers (for REST API and gRPC) and manipulates with two Brands entity definitions (as plain Go struct for REST API and Protobuf message for gRPC). Hopefully, the next chapter will fix it.

--

--