1# Getting Started 2 3This tutorial is meant to be an introduction to Tonic and assumes that you have basic [Rust] experience as well as an understanding of what [protocol buffers] are. If you don't, feel free to read up on the pages linked in this paragraph and come back to this tutorial once you feel you are ready! 4 5[rust]: https://www.rust-lang.org/ 6[protocol buffers]: https://developers.google.com/protocol-buffers/docs/overview 7 8## Prerequisites 9 10To run the sample code and walk through the tutorial, the only prerequisite is Rust itself. 11[rustup] is a convenient tool to install it, if you haven't already. 12 13[rustup]: https://rustup.rs 14 15## Project Setup 16 17For this tutorial, we will start by creating a new Rust project with Cargo: 18 19```shell 20$ cargo new helloworld-tonic 21$ cd helloworld-tonic 22``` 23 24`tonic` works on rust `1.39` and above as it requires support for the `async_await` 25feature. 26 27```bash 28$ rustup update 29$ rustup component add rustfmt 30``` 31 32## Defining the HelloWorld service 33 34Our first step is to define the gRPC _service_ and the method _request_ and _response_ types using 35[protocol buffers]. We will keep our `.proto` files in a directory in our crate's root. 36Note that Tonic does not really care where our `.proto` definitions live. 37 38```shell 39$ mkdir proto 40$ touch proto/helloworld.proto 41``` 42 43Then you define RPC methods inside your service definition, specifying their request and response 44types. gRPC lets you define four kinds of service methods, all of which are supported by Tonic. For this tutorial we will only use a simple RPC, if you would like to see a Tonic example which uses all four kinds please read the [routeguide tutorial]. 45 46[routeguide tutorial]: https://github.com/hyperium/tonic/blob/master/examples/routeguide-tutorial.md 47 48First we define our package name, which is what Tonic looks for when including your protos in the client and server applications. Lets give this one a name of `helloworld`. 49 50```proto 51syntax = "proto3"; 52package helloworld; 53``` 54 55Next we need to define our service. This service will contain the actual RPC calls we will be using in our application. An RPC contains an Identifier, a Request type, and returns a Response type. Here is our Greeter service, which provides the SayHello RPC method. 56 57```proto 58service Greeter { 59 // Our SayHello rpc accepts HelloRequests and returns HelloReplies 60 rpc SayHello (HelloRequest) returns (HelloReply); 61} 62``` 63 64Finally, we have to actually define those types we used above in our `SayHello` RPC method. RPC types are defined as messages which contain typed fields. Here is what that will look like for our HelloWorld application: 65 66```proto 67message HelloRequest { 68 // Request message contains the name to be greeted 69 string name = 1; 70} 71 72message HelloReply { 73 // Reply contains the greeting message 74 string message = 1; 75} 76``` 77 78Great! Now our `.proto` file should be complete and ready for use in our application. Here is what it should look like completed: 79 80```proto 81syntax = "proto3"; 82package helloworld; 83 84service Greeter { 85 rpc SayHello (HelloRequest) returns (HelloReply); 86} 87 88message HelloRequest { 89 string name = 1; 90} 91 92message HelloReply { 93 string message = 1; 94} 95``` 96 97## Application Setup 98 99Now that have defined the protobuf for our application we can start writing our application with Tonic! Let's first add our required dependencies to the `Cargo.toml`. 100 101```toml 102[package] 103name = "helloworld-tonic" 104version = "0.1.0" 105edition = "2018" 106 107[[bin]] # Bin to run the HelloWorld gRPC server 108name = "helloworld-server" 109path = "src/server.rs" 110 111[[bin]] # Bin to run the HelloWorld gRPC client 112name = "helloworld-client" 113path = "src/client.rs" 114 115[dependencies] 116tonic = "0.1.0-beta.1" 117prost = "0.6" 118tokio = { version = "0.2", features = ["macros"] } 119 120[build-dependencies] 121tonic-build = "0.1.0-beta.1" 122``` 123 124We include `tonic-build` as a useful way to incorporate the generation of our client and server gRPC code into the build process of our application. We will setup this build process now: 125 126## Generating Server and Client code 127 128At the root of your crate, create a `build.rs` file and add the following code: 129 130```rust 131fn main() -> Result<(), Box<dyn std::error::Error>> { 132 tonic_build::compile_protos("proto/helloworld.proto")?; 133 Ok(()) 134} 135``` 136 137This tells `tonic-build` to compile your protobufs when you build your Rust project. While you can configure this build process in a number of ways, we will not get into the details in this introductory tutorial. Please see the [tonic-build] documentation for details on configuration. 138 139[tonic-build]: https://github.com/hyperium/tonic/blob/master/tonic-build/README.md 140 141## Writing our Server 142 143Now that the build process is written and our dependencies are all setup, we can begin writing the fun stuff! We need to import the things we will be using in our server, including the protobuf. Start by making a file called `server.rs` in your `/src` directory and writing the following code: 144 145```rust 146use tonic::{transport::Server, Request, Response, Status}; 147 148use hello_world::greeter_server::{Greeter, GreeterServer}; 149use hello_world::{HelloReply, HelloRequest}; 150 151pub mod hello_world { 152 tonic::include_proto!("helloworld"); // The string specified here must match the proto package name 153} 154``` 155 156Next up, let's implement the Greeter service we previously defined in our `.proto` file. Here's what that might look like: 157 158```rust 159#[derive(Debug, Default)] 160pub struct MyGreeter {} 161 162#[tonic::async_trait] 163impl Greeter for MyGreeter { 164 async fn say_hello( 165 &self, 166 request: Request<HelloRequest>, // Accept request of type HelloRequest 167 ) -> Result<Response<HelloReply>, Status> { // Return an instance of type HelloReply 168 println!("Got a request: {:?}", request); 169 170 let reply = hello_world::HelloReply { 171 message: format!("Hello {}!", request.into_inner().name).into(), // We must use .into_inner() as the fields of gRPC requests and responses are private 172 }; 173 174 Ok(Response::new(reply)) // Send back our formatted greeting 175 } 176} 177``` 178 179Finally, let's define the Tokio runtime that our server will actually run on. This requires Tokio to be added as a dependency, so make sure you included that! 180 181```rust 182#[tokio::main] 183async fn main() -> Result<(), Box<dyn std::error::Error>> { 184 let addr = "[::1]:50051".parse()?; 185 let greeter = MyGreeter::default(); 186 187 Server::builder() 188 .add_service(GreeterServer::new(greeter)) 189 .serve(addr) 190 .await?; 191 192 Ok(()) 193} 194``` 195 196Altogether your server should look something like this once you are done: 197 198```rust 199use tonic::{transport::Server, Request, Response, Status}; 200 201use hello_world::greeter_server::{Greeter, GreeterServer}; 202use hello_world::{HelloReply, HelloRequest}; 203 204pub mod hello_world { 205 tonic::include_proto!("helloworld"); 206} 207 208#[derive(Debug, Default)] 209pub struct MyGreeter {} 210 211#[tonic::async_trait] 212impl Greeter for MyGreeter { 213 async fn say_hello( 214 &self, 215 request: Request<HelloRequest>, 216 ) -> Result<Response<HelloReply>, Status> { 217 println!("Got a request: {:?}", request); 218 219 let reply = hello_world::HelloReply { 220 message: format!("Hello {}!", request.into_inner().name).into(), 221 }; 222 223 Ok(Response::new(reply)) 224 } 225} 226 227#[tokio::main] 228async fn main() -> Result<(), Box<dyn std::error::Error>> { 229 let addr = "[::1]:50051".parse()?; 230 let greeter = MyGreeter::default(); 231 232 Server::builder() 233 .add_service(GreeterServer::new(greeter)) 234 .serve(addr) 235 .await?; 236 237 Ok(()) 238} 239``` 240 241You should now be able to run your HelloWorld gRPC server using the command `cargo run --bin helloworld-server`. This uses the [[bin]] we defined earlier in our `Cargo.toml` to run specifically the server. If have a gRPC GUI client such as [Bloom RPC] you should be able to send requests to the server and get back greetings! 242 243[bloom rpc]: https://github.com/uw-labs/bloomrpc 244 245## Writing our Client 246 247So now we have a running gRPC server, and that's great but how can our application communicate with it? This is where our client would come in. Tonic supports both client and server implementations. Similar to the server, we will start by creating a file `client.rs` in our `/src` directory and importing everything we will need: 248 249```rust 250use hello_world::greeter_client::GreeterClient; 251use hello_world::HelloRequest; 252 253pub mod hello_world { 254 tonic::include_proto!("helloworld"); 255} 256``` 257 258The client is much simpler than the server as we don't need to implement any service methods, just make requests. Here is a Tokio runtime which will make our request and print the response to your terminal: 259 260```rust 261#[tokio::main] 262async fn main() -> Result<(), Box<dyn std::error::Error>> { 263 let mut client = GreeterClient::connect("http://[::1]:50051").await?; 264 265 let request = tonic::Request::new(HelloRequest { 266 name: "Tonic".into(), 267 }); 268 269 let response = client.say_hello(request).await?; 270 271 println!("RESPONSE={:?}", response); 272 273 Ok(()) 274} 275``` 276 277That's it! Our complete client file should look something like below, if it doesn't please go back and make sure you followed along correctly: 278 279```rust 280use hello_world::greeter_client::GreeterClient; 281use hello_world::HelloRequest; 282 283pub mod hello_world { 284 tonic::include_proto!("helloworld"); 285} 286 287#[tokio::main] 288async fn main() -> Result<(), Box<dyn std::error::Error>> { 289 let mut client = GreeterClient::connect("http://[::1]:50051").await?; 290 291 let request = tonic::Request::new(HelloRequest { 292 name: "Tonic".into(), 293 }); 294 295 let response = client.say_hello(request).await?; 296 297 println!("RESPONSE={:?}", response); 298 299 Ok(()) 300} 301``` 302 303## Putting it all together 304 305At this point we have written our protobuf file, a build file to compile our protobufs, a server which implements our SayHello service, and a client which makes requests to our server. You should have a `proto/helloworld.proto` file, a `build.rs` file at the root of your project, and `src/server.rs` as well as a `src/client.rs` files. 306 307To run the server, run `cargo run --bin helloworld-server`. 308To run the client, run `cargo run --bin helloworld-client` in another terminal window. 309 310You should see the request logged out by the server in its terminal window, as well as the response logged out by the client in its window. 311 312Congrats on making it through this introductory tutorial! We hope that this walkthrough tutorial has helped you understand the basics of Tonic, and how to get started writing high-performance, interoperable, and flexible gRPC servers in Rust. For a more in-depth tutorial which showcases an advanced gRPC server in Tonic, please see the [routeguide tutorial]. 313