gRPC and HTTP Integration in Rust

It has became increasingly common to use gRPC for synchronous communication between microservices, its has shown to have good efficiency mainly because it uses Protocol Buffer as IDL and to serialize and deserilize the trafficked data. In this paper we will understand the basic concepts about gRPC and how we can use it with a practical example in Rust.
What is gRPC?
gRPC is standard for Google Remote Procedure Call, nowadays maintained by Cloud Native Computer project, we can bee define gRPC as
a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication.
The picture bellow exemplify the main idea of gRPC.

As we can see gRPC use client server pattern to allow communication between services over the network and we can write those in any program language.
We use Protocol Buffer to define our gRPC specifications, the messages that will be trafficked and the rpc methods, bellow we have a simple example of Protocol Buffer for gRPC specification:
syntax = "proto3";
package basic;
message SomethingRequest {
string name = 1;
}
message SomethingResponse {
uint64 id = 1;
}
service SomethingSerivce {
rpc Create(SomethingRequest) returns (SomethingResponse)
}
As we can see we have defined a request and response message and a rpc method, with this simple specification we are already able to create a gRPC server and a gRPC client. This specification we call Poto File and we create this file using the .proto extension. If you have never seen Protocol Buffer before, you are probably thing How can I use this .proto file in my project with my program language? My program language does not support this kind of file, and you are right, your program language does not support the .proto extension and she really shouldn't. After we declare our .proto file we need to use some tools to "convert" the Protocol Buffer specification to data structs and methods that is compatible with our program language. Let's see how its work.
Protocol Buffer works with a set of tools that we use to convert the specification, the main tools is the protobuf compiler called protoc and the other tools is kind of plugin for each program language. Lets say we have been working with GoLang, for example, we need install the protoc binary and the GoLang plugin, in Linux we can install those tools like this:
apt install -y protobuf-compiler && \
go install google.golang.org/protobuf/cmd/protoc-gen-go && \
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
Continuing with the GoLang example, after we installed the tools we can "convert" the .proto file to .go file like this:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
exemplo.proto
There are plugins for each program language and some have different approach to compile the proto files but all of than follow those main ideas.
To show haw we can use gRPC with RustLang we have been created a simple example, you can find it in this repository. In the next sections we will discuss a lit bit about it.
Creating our Protocol Buffer specification
First of all we need to declare our proto file, normally we keep the profo file in a folder inside our project, but depends on the integrations that our server will have, its common to store all the proto files in a different repository, but in our case we will keep the profo file in the project repository in a folder called protofiles. As our proto file will defined the service communication interface, is a best practice to create a way to versioning this proto files, a simple way to do that is inside the folder proto file create others folders to manage the versions, so we end up with something like this:
./
./protofiles/
./protofiles/v1/
./protofiles/v1/basic.proto
And our proto file will be like this:
syntax = "proto3";
package basic;
import "google/protobuf/empty.proto";
message Basic {
uint64 id = 1;
}
message CreateRequest {
}
message ListPagedRequest {
uint32 limit = 1;
uint32 offset = 2;
}
message ListResponse {
repeated Basic data = 1;
}
service Basics {
rpc Create(CreateRequest) returns (google.protobuf.Empty);
rpc ListPaged(ListPagedRequest) returns (ListResponse);
}
To compile proto files to Rust, there is a lot of ways to do that, but there is one that is most used for the community, normally we create a rust workspace project and in this workspace we create a library project call protos, in this library we configure a rust build script to compile our protos files using prost and tonic-build crates. To create rust build script we just create a build.rs file next to the Cargo.toml file of the protos library. You can check the build script and the prost output in the project repository.
Now as we create our Protocol Buffer specification we can create our server.
Crating the gRPC Server
Now we will create a binary to bee our gRPC Server and in this binary we will use the protos library like a crate. You can just follow the project implementation to see how we have created the gRPC server.
HTTP Server
Is very common to integrate HTTP API's with gRPC servers, because of that we have crated a HTTP Server to received HTTP request and call the gRPC server to handle the request.
It could sound a lite too much, you could ask Why we just handle the request in the HTTP API instead of call another server over network? So the answer is simple, in a big projects with a lot of features is very common to segregate the responsibility of some features to a specific microservice and only this microservice will be responsible to handle the requests about those features, as engineer we know that, there is no free launch, each approach comes with its pros and cons, but in some context, for some projects the pros is much better than the cons.
To create the HTTP API we used the actix-web crate and to create a nice OpenApi documentation we used utoipa crate, you can check the HTTP server implementation in the project repository.
To execute the HTTP server we need to ensure the gRPC server is running in the expected port.
So that is for today, I hope this simple article help you to understand a lit bit more about gRPC and if you like this article consider leaving a clap.