What it is
Tonic is a native Rust gRPC implementation built on Tokio, Hyper and Tower, with code generation from .proto files and full streaming support.
A build script compiles .proto files into Rust traits and clients. Implement the generated trait for the server; the client is ready to use.
Installation
cargo add tonic prost tokio --features tokio/fullGetting started
The smallest useful thing you can do with it, and what each part means.
rust
// build.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/library.proto")?;
Ok(())
}
// server
#[tonic::async_trait]
impl BookService for MyService {
async fn get_book(
&self,
request: Request<GetBookRequest>,
) -> Result<Response<Book>, Status> {
let id = request.into_inner().id;
self.store.find(&id).await
.map(Response::new)
.ok_or_else(|| Status::not_found(format!("book {id}")))
}
}
Server::builder()
.add_service(BookServiceServer::new(MyService::default()))
.serve("[::1]:50051".parse()?)
.await?;Advanced usage
Where the library earns its place over a simpler alternative.
rust
type BookStream = Pin<Box<dyn Stream<Item = Result<Book, Status>> + Send>>;
async fn list_books(
&self,
_request: Request<ListRequest>,
) -> Result<Response<Self::ListBooksStream>, Status> {
let (tx, rx) = tokio::sync::mpsc::channel(32);
let store = self.store.clone();
tokio::spawn(async move {
for book in store.all().await {
// Stop work if the client disconnects.
if tx.send(Ok(book)).await.is_err() { break; }
}
});
Ok(Response::new(Box::pin(ReceiverStream::new(rx))))
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Build fails: could not find protoc
- tonic-build needs the protobuf compiler installed, or enable a vendored protoc feature.
- Client hangs indefinitely
- No deadline was set. Apply a timeout on the request or via a Tower layer.
Best practices
- Return Status with a meaningful code; clients branch on codes, not messages.
- Bound the channel in streaming handlers so a slow client applies backpressure.
- Reuse one Channel for the client — it multiplexes over HTTP/2.
- Reuse Tower layers from the Axum ecosystem for auth, timeouts and tracing.
Background
Why it exists, and what it was reacting to.
Tonic gave Rust a first-class gRPC stack with no C++ dependency, and because it builds on Tower, its middleware is shared with Axum and the rest of the ecosystem.
