tonic-rest-build
Build-time REST codegen from protobuf google.api.http annotations for Tonic + Axum.
Part of the tonic-rest ecosystem — define your API once in proto files, get gRPC, REST, and OpenAPI 3.1.
Reads a compiled proto FileDescriptorSet, extracts google.api.http annotations,
and generates Axum route handler code that calls through Tonic service traits — keeping
proto files as the single source of truth for both gRPC and REST APIs.
Key Features
- Proto as single source of truth — one definition drives gRPC, REST endpoints, and OpenAPI docs
- Build-time codegen — Axum handlers generated from
FileDescriptorSetat compile time; zero runtime overhead or reflection - Standard annotations — uses
google.api.httpbindings, not a proprietary DSL - Zero-config auto-discovery — scans the descriptor set for any service with HTTP annotations; no manual package listing required
- SSE for server streaming — streaming RPCs are automatically exposed as Server-Sent Events endpoints
- Serde auto-wiring —
configure_prost_serdediscovers WKT fields and applies#[serde(with)]attributes automatically
How It Works
Annotate your proto service with google.api.http:
service ItemService {
rpc CreateItem(CreateItemRequest) returns (Item) {
option (google.api.http) = { post: "/v1/items" body: "*" };
}
rpc GetItem(GetItemRequest) returns (Item) {
option (google.api.http) = { get: "/v1/items/{item_id}" };
}
}
The generated code calls through Tonic service traits — sharing auth, validation, and business logic with gRPC handlers:
async
Quick Start
[]
= "0.1"
[]
= "0.1"
= "0.14"
Zero-config build.rs — auto-discovers packages from the descriptor set:
use ;
const PROTO_FILES: & = &;
const PROTO_INCLUDES: & = &;
Configuration
Explicit package mapping (e.g., when using pub use v1::*; re-exports):
let config = new
.package
.package
.wrapper_type
.extension_type
.public_methods
.sse_keep_alive_secs;
RestCodegenConfig Options
| Method | Default | Description |
|---|---|---|
.package(proto, rust) |
auto-discover | Proto package → Rust module mapping |
.extension_type(path) |
None |
Extension type for Axum Extension<T> extraction |
.public_methods(list) |
empty | Methods whose paths skip auth middleware |
.wrapper_type(path) |
None |
Rust type for single-field wrapper messages (UUID) |
.proto_root(path) |
"crate" |
Root module for proto types |
.runtime_crate(path) |
"tonic_rest" |
Path to runtime types |
.sse_keep_alive_secs(n) |
15 |
SSE keep-alive interval |
.extra_forwarded_headers(&[..]) |
empty | Extra HTTP headers to forward to gRPC metadata |
Feature Flags
| Feature | Default | Description |
|---|---|---|
helpers |
on | dump_file_descriptor_set and configure_prost_serde helpers (adds prost-build dep) |
Serde Attribute Helper
Auto-discover proto fields and apply #[serde(with)] attributes:
use configure_prost_serde;
configure_prost_serde;
Runtime Dependencies
The generated handler code references types from these crates — ensure they are
in your [dependencies]:
| Crate | Used for |
|---|---|
tonic-rest |
RestError, build_tonic_request, sse_error_event |
tonic |
tonic::Status, tonic::Request, service traits |
axum |
Router, extractors, Json, Query, SSE |
futures |
Stream, StreamExt (streaming endpoints only) |
serde_json |
Json extractor/response |
Generated Code
For each service with HTTP annotations:
{service}_rest_router(service: Arc<S>) -> Router— route registration- Per-method handler functions with proper extractors
PUBLIC_REST_PATHS: &[&str]— paths that bypass authentication middlewareall_rest_routes(...)— combined router for all services
Handler Variants
| HTTP Method | Body | Response |
|---|---|---|
| POST/PUT/PATCH | Json<T> |
Json<Response> |
| GET | Query<T> |
Json<Response> |
| DELETE | T::default() |
StatusCode::NO_CONTENT |
| GET (streaming) | Query<T> |
Sse<impl Stream> |
Planned
additional_bindings: ProtoHttpRule.additional_bindings(multiple REST mappings per gRPC method) is not supported. Only the primary HTTP binding is processed.- Partial body selectors: Only
body: "*"(full body) andbody: ""(no body) are supported. Thebody: "field_name"partial body binding from the gRPC-HTTP transcoding spec is not implemented. - Repeated WKT fields:
configure_prost_serdedoes not wire serde adapters for lists of well-known types (e.g.repeated google.protobuf.Timestamp). Single fields of these types work correctly.
For a complete end-to-end example with proto files, build.rs, REST handlers, and OpenAPI generation,
see auth-service-rs.
Companion Crates
| Crate | Purpose | Cargo section |
|---|---|---|
| tonic-rest-core | Shared descriptor types | internal |
| tonic-rest | Runtime types | [dependencies] |
| tonic-rest-build (this) | Build-time codegen | [build-dependencies] |
| tonic-rest-openapi | OpenAPI 3.1 generation | CLI / CI |
Compatibility
| tonic-rest-build | prost / prost-build | tonic | axum | MSRV |
|---|---|---|---|---|
| 0.1.x | 0.14 | 0.14 | 0.8 | 1.85 |
License
MIT OR Apache-2.0