title: gRPC Support sidebar_position: 12 id: grpc_support license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Fory can generate Rust gRPC service companions for schemas that define services. The generated code uses tonic for transport and Fory for request and response payload serialization.
Use this mode when every RPC peer is generated from the same Fory IDL, protobuf IDL, or FlatBuffers IDL and you want gRPC transport semantics with Fory payload encoding. Use standard protobuf gRPC code generation when clients or tools must consume protobuf message bytes directly.
Add tonic and bytes to the crate that compiles the generated service files. Fory Rust crates do not add gRPC as a hard dependency. Add tokio for async servers and clients, and tokio-stream when your service implementation needs to build streaming responses.
[dependencies] fory = "1.1.0" bytes = "1" tonic = { version = "0.14", features = ["transport"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } tokio-stream = "0.1"
Use dependency versions that are compatible with the rest of your service stack.
Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers rpc_service definitions. A Fory IDL service looks like this:
package demo.greeter; message HelloRequest { string name = 1; } message HelloReply { string reply = 1; } service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); }
Generate Rust model and gRPC companion code with --grpc:
foryc service.fdl --rust_out=./generated/rust --grpc
For this schema, the Rust generator emits:
| File | Purpose |
|---|---|
demo_greeter.rs | Fory model types and registration helpers |
demo_greeter_service.rs | Async service trait and gRPC path constants |
demo_greeter_service_grpc.rs | tonic client, server wrapper, and Fory codec |
Add the generated files to your crate root:
pub mod demo_greeter; pub mod demo_greeter_service; pub mod demo_greeter_service_grpc;
Implement the generated async trait and add the generated server wrapper to a normal tonic server.
use demo_greeter::{HelloReply, HelloRequest}; use demo_greeter_service::Greeter; use demo_greeter_service_grpc::greeter_server::GreeterServer; use tonic::{Request, Response, Status}; #[derive(Default)] struct MyGreeter; #[tonic::async_trait] impl Greeter for MyGreeter { async fn say_hello( &self, request: Request<HelloRequest>, ) -> Result<Response<HelloReply>, Status> { let request = request.into_inner(); Ok(Response::new(HelloReply { reply: format!("Hello, {}", request.name), })) } } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let addr = "[::1]:50051".parse()?; tonic::transport::Server::builder() .add_service(GreeterServer::new(MyGreeter::default())) .serve(addr) .await?; Ok(()) }
Generated request and response types are serialized by the generated service code, so service implementations do not perform manual Fory registration.
Use the generated tonic client:
use demo_greeter::HelloRequest; use demo_greeter_service_grpc::greeter_client::GreeterClient; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let mut client = GreeterClient::connect("http://[::1]:50051").await?; let response = client .say_hello(HelloRequest { name: "Fory".to_string(), }) .await?; println!("{}", response.into_inner().reply); Ok(()) }
tonic still owns channel configuration, TLS, deadlines, metadata, interceptors, and transport lifecycle.
Fory service definitions can use unary, server-streaming, client-streaming, and bidirectional streaming RPC shapes:
service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); rpc LotsOfReplies (HelloRequest) returns (stream HelloReply); rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply); rpc Chat (stream HelloRequest) returns (stream HelloReply); }
Generated Rust code follows tonic conventions:
tonic::Request<T> and return tonic::Response<U>.Result<U, tonic::Status>.tonic::Streaming<T>.Use the generated trait signatures as the source of truth for the concrete associated stream types in your service implementation.
Generated Rust gRPC payloads must be Send + 'static so tonic can move request and response values across async tasks. If a schema uses non-thread-safe reference metadata for a request or response type, Rust gRPC generation rejects that service. Use thread-safe reference shapes for gRPC payloads, or keep the non-thread-safe type out of the RPC boundary.
The generated service companion only supplies Fory serialization and tonic bindings. Operational behavior remains standard tonic behavior:
tonic or bytes CratesAdd the dependencies shown above to the crate that compiles the generated service files.
UNIMPLEMENTEDConfirm that the generated server wrapper was added with Server::builder().add_service(...), and that the client and server were generated from the same package, service, and method names.
Rust gRPC payloads must be Send + 'static. Change the request or response schema to use thread-safe reference shapes, or wrap the non-thread-safe data in a type that is not part of the gRPC payload.
Fory gRPC companions do not use protobuf wire encoding for messages. Use a Fory-generated client for Fory-generated services, or provide a separate protobuf service endpoint for generic protobuf clients.