Background & Introduction

RPC vs JSON vs SOAP

Once I learn about gRPC and Thrift, it is hard to go back to using the more transitional JSON-based REST API or SOAP API.

The two well-known RPC frameworks, gRPC, and Thrift, have many similarities. The former originates from Google while the latter originates from Facebook. Both of them are easy to use, have great support for a wide range of programming languages, and both are performant.

The most valuable feature is code generation in multiple languages and also server-side reflection. These make the API essentially type-safe. With server-side reflection, it makes it much easier to explore the schema definition of the API without having to read and understand the implementation.

Grpc vs Thrift

Apache Thrift historically has been a popular choice. However, in recent years, due to a lack of continuous support from Facebook, and fragmentation with the fork of fbthrift, it slowly lost popularity.

In the meantime, gRPC has caught up with more and more features with a healthier ecosystem.

Comparison between GRPC (blue) with Apache Thrift (red). Google Trends

GitHub star history between gRPC, fbThrift, and Apache Thrift. https://star-history.com

As of today, unless your application is affiliated with Facebook in someway, there is no good reason to consider Thrift.

How about GraphQL?

GraphQL is another framework initiated from Facebook. It shares many similarities with the two RPC frameworks above.

One of the biggest pain points in mobile API development is that some users never upgrade their app. Because we want to maintain backward compatibility, we either have to keep old unused fields in the API or create multiple versions of the API. One motivation of GraphQL was to solve that problem. It is designed to be a “query language” and allow the client to specify what data fields that it needs. This makes it easier to handle backward compatibility.

GraphQL has great value in developing mobile APIs as well as public facing APIs (such as GitHub). Since, in both cases, we could not easily control the client behavior.

However, if we are building an API for the web frontend or API for internal backend services, there is little benefit of choosing GraphQL over gRPC.

Rust

Above is a small overview of the networking frameworks so far. Besides networking, we also need to decide on a language for the application server.

Based on Stack Overflow Survey: “For the sixth-year, Rust is the most loved language.” Its type-safety, elegant memory management, and wide community support, and performance, all make Rust a very attractive and promising programming language for backend service developments despite the relatively steeper learning curve.

Rust is the most loved language. Stack Overflow Survey 2021

We also start seeing wider and wider adoption of Rust in the industry: Facebook, Dropbox, Yelp, AWS, Google, etc. It is clear that Rust will continue to grow and is here to stay.

That’s what we’ll look in in today’s tutorial — building a small server with gRPC in Rust.

Install Rust

Install Rust with the following:

$ curl --proto '=https' --tlsv1.2 -sSf <https://sh.rustup.rs> | sh

If you’ve previously installed Rust, we can update it via:

$ rustup update stable

Let’s double-check the installed version of rustc (the Rust compiler) and cargo (the Rust package manager):

$ rustc --version

rustc 1.60.0 (7737e0b5c 2022-04-04)

$ cargo --version

cargo 1.60.0 (d1fd9fe2c 2022-03-01)

For more information about installation, checkout https://www.rust-lang.org/tools/install.

Create a Rust Project

Run the following to create a new “Hello World” project:

$ cargo new rust_grpc_demo --bin

Let’s compile and run the program:

$ cd rust_grpc_demo

$ cargo run

   Compiling rust_grpc_demo v0.1.0 (/Users/yuchen/Documents/rust_grpc_demo)
    Finished dev [unoptimized + debuginfo] target(s) in 1.75s
     Running `target/debug/rust_grpc_demo`
Hello, world!

This shows the file structures we have so far:

$ find . -not -path "./target*" -not -path "./.git*" | sed -e "s/[^-][^\/]*\//  |/g" -e "s/|\([^ ]\)/| - \1/"

  |-Cargo.toml
  |-Cargo.lock
  |-src
  |  |-main.rs

Define gRPC Interface

gRPC uses Protocol Buffers for serializing and deserializing data. Let’s define the server API in a .proto file.

$ mkdir proto
$ touch proto/bookstore.proto

We define a book store service, with only one method: provide a book id, and return some details about the book.

We will create our gRPC service with tonic. Add the following dependencies to the Cargo.toml file:

To generate Rust code from bookstore.proto, we use tonic-build in the crate’s build.rs build-script.

$ touch build.rs

Add the following to the build.rs file:

One thing specific to point out that we added this .out_dir(“./src”) to change the default output directory to the src directory so that we can see the generated file easier for the purpose of this post.

One more thing before we are ready to compile.tonic-build depends on the Protocol Buffers compiler to parse .proto files into a representation that can be transformed into Rust. Let’s install protobuf:

$ brew install protobuf

And double check that the protobuf compiler is installed properly:

$ protoc --version

libprotoc 3.19.4

Ready to compile:

$ cargo build

    Finished dev [unoptimized + debuginfo] target(s) in 0.31s

With this, we should have a file src/bookstore.rs generated. At this point, our file structure should look like this:

  | - Cargo.toml
  | - proto
  |  | - bookstore.proto
  | - Cargo.lock
  | - build.rs
  | - src
  |  | - bookstore.rs
  |  | - main.rs

Implement the Server

Finally, time to put the service together. Replace the main.rs with the following:

As we can see, for the sake of simplicity, we don’t really have a database of book setup. In this endpoint, we simply return a fake book.

Time to run the server:

$ cargo run

   Compiling rust_grpc_demo v0.1.0 (/Users/yuchen/Documents/rust_grpc_demo)
    Finished dev [unoptimized + debuginfo] target(s) in 2.71s
     Running `target/debug/rust_grpc_demo`
Bookstore server listening on [::1]:50051

Nice we have our gRPC server in Rust up and running!

Bonus: Server Reflection

As stated at the beginning, I was first impressed by gRPC initially because of its capability of doing server reflection. Not only is it handy during service development, but it also makes communication with frontend engineers a lot easier. So, it wouldn’t be complete to conclude this tutorial without explaining how to add that for the Rust Server.

Add the following to the dependencies:

tonic-reflection = "0.4.0"

Update build.rs. The lines that need to be changed are marked with // Add this comment.

And finally, update the main.rs to the following.

Testing The gRPC Server

There are many GUI clients to play with gRPC Server, such as Postman, Kreya, bloomrpc, grpcox, etc. To keep things simple for today, we will use a command line tool grpc_cli.

To install:

$ brew install grpc

And to test out our first gRPC endpoint:

$ grpc_cli call localhost:50051 bookstore.Bookstore.GetBook "id: 'test-book-id'"

connecting to localhost:50051
Received initial metadata from server:
date : Sun, 08 May 2022 20:15:39 GMT
id: "test-book-id"
name: "Zero to One"
author: "Peter"
year: 2014
Rpc succeeded with OK status

Looks like it works! And that, my friend, is how we build a gRPC server in Rust.

That’s it for today. Thanks for reading and happy coding! As usual, the source code is available on GitHub.

This article is also available at https://betterprogramming.pub/be2c52f0860e.