Creating A GRPC Endpoint With Rust

Creating A GRPC Endpoint With Rust

ยท

5 min read


Introduction

Hello! ๐Ÿ˜Ž

In this tutorial, I will show you how to use Rust and Proto to create a simple hello world endpoint using gRPC. ๐Ÿ‘€


Requirements

  • Rust installed

Creating The Project

First, we need to create the project! Assuming you already have Rust installed, just run the following command to create a new rust application:

cargo new rustgrpc && cd rustgrpc

Feel free to change the app name; the above command will create a new rust application and change directories to the application directory.

Next, we need to declare the dependencies that will be used for this project. Open up the "Cargo.toml" file and replace the contents with the following:

[package]
name = "rust"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tonic = "0.7"
tokio = { version = "1", features = ["full"] }
prost = "0.10"

[build-dependencies]
tonic-build = "0.7"

This example makes use of Tonic, which is a rust implementation of gRPC. It is fast, high performance, open source, general RPC framework that puts mobile and HTTP/2 first! Now that the dependencies have been defined, we can now move on to actually coding the application!


Creating The Proto File

First, we will create the proto file to handle the request and response messages. A proto file helps the Client and the Server understand the messages that are sent and received.

Create a new directory called "protos" in your current directory and create a new file called "hello.proto". Fill the file with the following contents:

syntax = "proto3";

package hello;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Nothing too complicated; the syntax declares the version of proto we want to use, in this case, the latest version which is proto3. The package declares the namespace, in this case, hello. Next, we define the service which takes a HelloRequest request (name) and returns HelloReply (message), to keep things simple only one variable is declared for each.

Next, we need to create a build file to help build the project.


Creating The Build File

Now that the proto file has been defined next we need to create a build file to build the project. Create a new file in the current directory called "build.rs" and populate it with the following contents:

fn main() -> Result<(), Box<dyn std::error::Error>> {
  tonic_build::compile_protos("protos/hello.proto")?;
  Ok(())
}

Again nothing too complicated here; we tell tonic where to find the above proto files so that it can be built.

Next, we will write the final code for the example project.


Writing The Main Code

Finally, we will write the code for the main function of this application. Open up "src/main.rs" and delete the boilerplate code.

First, we will declare the imports and modules like so:

use tonic::{transport::Server, Request, Response, Status};

pub mod hello {
  tonic::include_proto!("hello");
}

use hello::{greeter_server::{Greeter, GreeterServer}, HelloReply, HelloRequest};

The first line imports things from the tonic module, after that we declare the hello module which includes the hello.proto that we wrote earlier, finally, we import the needed things from the hello proto file.

Next, we need to define a struct that will implement our service, add the following code below the above:

#[derive(Default)]
pub struct MyGreeter {}

Next, we need to create an implementation in order to handle the hello request and response, add the following to the above:

#[tonic::async_trait]
impl Greeter for MyGreeter {
  async fn say_hello(
    &self,
    request: Request<HelloRequest>
  ) -> Result<Response<HelloReply>, Status> {
    let reply = hello::HelloReply {
      message: format!("Hello {}!", request.into_inner().name)
    };
    Ok(Response::new(reply))
  }
}

The function takes a HelloRequest and returns a HelloReply, in simple terms "Hello (Request name)".

Finally, we need to define a main function that will start the server and listen for requests add the following to the above:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
  let addr = "[::1]:50051".parse()?;
  let greeter = MyGreeter::default();

  Server::builder()
    .add_service(GreeterServer::new(greeter))
    .serve(addr)
    .await?;

  Ok(())
}

Done!

Now all we need to do now is build and run the project! ๐Ÿฅณ


Running The Application

Finally, all we need to do is build and run the project; the project can be built via the following command:

cargo build

To run the application use the following command: (you might need grpcurl if you don't have it already)

cargo run

Now that the server is running, you can test the request with the following command (make sure to run the command in the same directory as the application):

grpcurl -plaintext -proto protos/hello.proto -d '{"name": "World"}' [::]:50051 hello.Greeter/SayHello

If all has gone right, you should see the response along the lines of "Hello World!" in the terminal! Like the following image:

Image showing application running


Conclusion

Here I have shown a basic example of using Rust and Proto to create a simple hello world endpoint. I hope this tutorial has helped you the same I had fun writing it!

As always, you can find the example code for this tutorial at my Github: https://github.com/ethand91/rust-grpc

Happy Coding! ๐Ÿ˜Ž


Like my work? I post about a variety of topics, if you would like to see more please like and follow me. Also I love coffee.

โ€œBuy Me A Coffeeโ€

If you are looking to learn Algorithm Patterns to ace the coding interview I recommend the [following course](https://algolab.so/p/algorithms-and-data-structure-video-course?affcode=1413380_bzrepgch

Did you find this article valuable?

Support Ethan by becoming a sponsor. Any amount is appreciated!

ย