Skip to main content

Crate tonic_rest_build

Crate tonic_rest_build 

Source
Expand description

§tonic-rest-build

Crates.io docs.rs License MSRV

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 FileDescriptorSet at compile time; zero runtime overhead or reflection
  • Standard annotations — uses google.api.http bindings, 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-wiringconfigure_prost_serde discovers 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 fn rest_create_item<S: ItemService>(
    State(service): State<Arc<S>>,
    headers: HeaderMap,
    Json(body): Json<CreateItemRequest>,
) -> Result<Json<Item>, RestError> {
    let req = build_tonic_request::<_, ()>(body, &headers, None);
    let response = service.create_item(req).await?;
    Ok(Json(response.into_inner()))
}

§Quick Start

[dependencies]
tonic-rest = "0.1"

[build-dependencies]
tonic-rest-build = "0.1"
prost-build = "0.14"

Zero-config build.rs — auto-discovers packages from the descriptor set:

use tonic_rest_build::{RestCodegenConfig, generate, dump_file_descriptor_set};

const PROTO_FILES: &[&str] = &["proto/service.proto"];
const PROTO_INCLUDES: &[&str] = &["proto"];

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();
    let descriptor_path = format!("{out_dir}/file_descriptor_set.bin");

    // Phase 1: Compile protos → descriptor set
    let descriptor_bytes = dump_file_descriptor_set(PROTO_FILES, PROTO_INCLUDES, &descriptor_path);

    // Phase 2: Compile protos → Rust (prost/tonic)
    let mut config = prost_build::Config::new();
    config.file_descriptor_set_path(&descriptor_path);
    config.compile_protos(PROTO_FILES, PROTO_INCLUDES).unwrap();

    // Phase 3: Generate REST routes
    let rest_config = RestCodegenConfig::new();
    let code = generate(&descriptor_bytes, &rest_config).unwrap();
    std::fs::write(format!("{out_dir}/rest_routes.rs"), code).unwrap();
}

§Configuration

Explicit package mapping (e.g., when using pub use v1::*; re-exports):

let config = RestCodegenConfig::new()
    .package("auth.v1", "auth")
    .package("users.v1", "users")
    .wrapper_type("crate::core::Uuid")
    .extension_type("my_app::AuthInfo")
    .public_methods(&["Login", "SignUp"])
    .sse_keep_alive_secs(30);

§RestCodegenConfig Options

MethodDefaultDescription
.package(proto, rust)auto-discoverProto package → Rust module mapping
.extension_type(path)NoneExtension type for Axum Extension<T> extraction
.public_methods(list)emptyMethods whose paths skip auth middleware
.wrapper_type(path)NoneRust 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)15SSE keep-alive interval
.extra_forwarded_headers(&[..])emptyExtra HTTP headers to forward to gRPC metadata

§Feature Flags

FeatureDefaultDescription
helpersondump_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 tonic_rest_build::configure_prost_serde;

configure_prost_serde(
    &mut config,
    &descriptor_bytes,
    PROTO_FILES,
    "crate::serde_wkt",
    &[(".google.protobuf.Timestamp", "opt_timestamp")],
    &[(".my.v1.UserRole", "user_role")],
);

§Runtime Dependencies

The generated handler code references types from these crates — ensure they are in your [dependencies]:

CrateUsed for
tonic-restRestError, build_tonic_request, sse_error_event
tonictonic::Status, tonic::Request, service traits
axumRouter, extractors, Json, Query, SSE
futuresStream, StreamExt (streaming endpoints only)
serde_jsonJson 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 middleware
  • all_rest_routes(...) — combined router for all services

§Handler Variants

HTTP MethodBodyResponse
POST/PUT/PATCHJson<T>Json<Response>
GETQuery<T>Json<Response>
DELETET::default()StatusCode::NO_CONTENT
GET (streaming)Query<T>Sse<impl Stream>

§Planned

  • additional_bindings: Proto HttpRule.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) and body: "" (no body) are supported. The body: "field_name" partial body binding from the gRPC-HTTP transcoding spec is not implemented.
  • Repeated WKT fields: configure_prost_serde does 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

CratePurposeCargo section
tonic-rest-coreShared descriptor typesinternal
tonic-restRuntime types[dependencies]
tonic-rest-build (this)Build-time codegen[build-dependencies]
tonic-rest-openapiOpenAPI 3.1 generationCLI / CI

§Compatibility

tonic-rest-buildprost / prost-buildtonicaxumMSRV
0.1.x0.140.140.81.85

§License

MIT OR Apache-2.0


§API Reference

Structs§

ProstSerdeConfig
Builder for configuring prost serde attributes.
RestCodegenConfig
Configuration for REST route code generation.

Enums§

GenerateError
Error returned by generate.

Functions§

configure_prost_serde
Configure prost serde attributes by scanning a FileDescriptorSet.
configure_prost_serde_with_options
Configure prost serde attributes with custom rename_all strategy.
dump_file_descriptor_set
Invoke protoc to produce a binary FileDescriptorSet.
generate
Generate REST route code from a compiled proto file descriptor set.
try_configure_prost_serde
Fallible version of configure_prost_serde.
try_configure_prost_serde_with_options
Fallible version of configure_prost_serde_with_options.
try_dump_file_descriptor_set
Fallible version of dump_file_descriptor_set.