Implementing a gRPC Server
Serve unary RPC methods.
Implementing a gRPC Server is a free Learn Rust Coding lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Generated Server Trait
For a service named Greeter, tonic generates a trait in greeter_server. You implement it on your own struct to provide behavior.
The trait is async via #[tonic::async_trait], so each method is an async fn returning a Result.
use greeter::v1::greeter_server::{Greeter, GreeterServer};
use greeter::v1::{HelloRequest, HelloReply};Request and Response Wrappers
Methods take a tonic::Request<T> and return a tonic::Response<T>. These wrappers carry metadata, extensions, and the inner message.
Call .into_inner() to get the decoded message, and Response::new(..) to build a reply.
let req: HelloRequest = request.into_inner();
let reply = HelloReply { message: format!("Hi {}", req.name) };
Ok(Response::new(reply))A Server Struct
Define a struct to hold any shared state, like a database pool. Often it derives Default when stateless.
You will implement the generated trait on this struct.
#[derive(Default)]
pub struct MyGreeter {}Implementing a Unary Method
Annotate the impl block with #[tonic::async_trait] and implement each rpc as an async fn matching the generated signature.
Return Ok(Response::new(reply)) on success.
#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(&self, request: Request<HelloRequest>)
-> Result<Response<HelloReply>, Status> {
let name = request.into_inner().name;
Ok(Response::new(HelloReply { message: format!("Hello {name}") }))
}
}Returning Errors with Status
Errors are returned as tonic::Status, which maps to a gRPC status code. Use constructors like Status::invalid_argument or Status::not_found.
The message string is sent to the client alongside the code.
if name.is_empty() {
return Err(Status::invalid_argument("name must not be empty"));
}Building and Serving
Use tonic::transport::Server in an async main. Add your service wrapped in its generated ...Server type, then call serve with a socket address.
The #[tokio::main] macro provides the runtime.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse()?;
Server::builder()
.add_service(GreeterServer::new(MyGreeter::default()))
.serve(addr)
.await?;
Ok(())
}Server Streaming Responses
A server-streaming method returns a stream type. tonic uses an associated type plus a boxed stream; an mpsc channel is a common way to feed messages.
You return a ReceiverStream wrapped in a Response.
use tokio_stream::wrappers::ReceiverStream;
let (tx, rx) = tokio::sync::mpsc::channel(8);
tokio::spawn(async move { tx.send(Ok(reply)).await.ok(); });
Ok(Response::new(ReceiverStream::new(rx)))Reading Metadata
Request metadata holds headers like auth tokens. Access it with request.metadata() before consuming the body.
Keys are ASCII and case-insensitive; values are returned as MetadataValue.
if let Some(token) = request.metadata().get("authorization") {
// validate token
} else {
return Err(Status::unauthenticated("missing token"));
}Interceptors
An interceptor runs before each request, ideal for auth or logging. It receives the Request and returns it or a Status error.
Attach it with with_interceptor when adding the service.
fn auth(req: Request<()>) -> Result<Request<()>, Status> {
match req.metadata().get("authorization") {
Some(_) => Ok(req),
None => Err(Status::unauthenticated("no token")),
}
}
// .add_service(GreeterServer::with_interceptor(svc, auth))Graceful Shutdown
Use serve_with_shutdown to stop accepting connections when a future resolves, such as a Ctrl-C signal.
In-flight requests finish before the server exits, avoiding abrupt drops.
Server::builder()
.add_service(GreeterServer::new(MyGreeter::default()))
.serve_with_shutdown(addr, async {
tokio::signal::ctrl_c().await.ok();
})
.await?;Shared State
To share mutable state across requests, store it behind Arc and a synchronization primitive like tokio::sync::Mutex inside your server struct.
tonic clones the service per connection, so cheap-to-clone shared handles are the right pattern.
use std::sync::Arc;
use tokio::sync::Mutex;
#[derive(Default)]
pub struct MyGreeter {
hits: Arc<Mutex<u64>>,
}Quick Check
How do gRPC methods report failures in tonic?
Recap
You implemented the generated server trait: unwrapping requests, returning responses and Status errors, serving with tokio, streaming responses, reading metadata, adding interceptors, graceful shutdown, and sharing state via Arc.
Next you will build a client to call this server.
Frequently asked questions
Is the “Implementing a gRPC Server” lesson free?
Yes — the full text of “Implementing a gRPC Server” is free to read here on the web, and the Learn Rust Coding course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.
What will I learn in “Implementing a gRPC Server”?
Serve unary RPC methods. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Learn Rust Coding?
No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Implementing a gRPC Server” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Learn Rust Coding lesson?
Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Protobuf and Service Definitions
- Generating Code with tonic-build
- Implementing a gRPC Server
- Calling from a gRPC Client