| Crates.io | connectrpc-axum-build |
| lib.rs | connectrpc-axum-build |
| version | 0.0.2 |
| created_at | 2025-09-09 14:08:20.723788+00 |
| updated_at | 2025-09-12 09:19:20.251258+00 |
| description | Code generation for connectrpc-axum |
| homepage | |
| repository | https://github.com/washanhanzi/connectrpc-axum |
| max_upload_size | |
| id | 1830925 |
| size | 93,047 |
Axum-style Connect RPC server.
Work in progress.
Add a build script to generate code from your .proto files.
// build.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
// EITHER: Connect-only
// connectrpc_axum_build::compile_dir("proto").compile()?;
// OR: Connect + Tonic (enable the "tonic" feature on the build crate)
connectrpc_axum_build::compile_dir("proto").with_tonic().compile()?;
Ok(())
}
Use any number of FromRequestParts extractors first, and end with ConnectRequest<T>.
use axum::{extract::{Query, State}, Router};
use connectrpc_axum::prelude::*;
#[derive(Clone, Default)]
struct AppState;
#[derive(serde::Deserialize)]
struct Pagination { page: usize, per_page: usize }
// Multiple extractors + Connect body
async fn say_hello(
Query(_p): Query<Pagination>,
State(_s): State<AppState>,
ConnectRequest(req): ConnectRequest<HelloRequest>,
) -> Result<ConnectResponse<HelloResponse>, ConnectError> {
Ok(ConnectResponse(HelloResponse { message: format!("Hello, {}!", req.name.unwrap_or_default()) }))
}
// Minimal handler (no state)
async fn say_hello_simple(
ConnectRequest(req): ConnectRequest<HelloRequest>,
) -> Result<ConnectResponse<HelloResponse>, ConnectError> {
Ok(ConnectResponse(HelloResponse { message: format!("Hi, {}!", req.name.unwrap_or_default()) }))
}
// Build routes via the generated service builder (no manual paths)
let router = helloworldservice::HelloWorldServiceBuilder::new()
.say_hello(say_hello)
.say_hello_stream(say_hello_simple)
.with_state(AppState::default())
.build();
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, tower::make::Shared::new(router)).await?;
Enable features in Cargo.toml:
[dependencies]
connectrpc-axum = { version = "*", features = ["tonic"] }
[build-dependencies]
connectrpc-axum-build = { version = "*", features = ["tonic"] }
build.rs:
connectrpc_axum_build::compile_dir("proto").with_tonic().compile()?;
Use the generated Tonic-compatible builder and single-port dispatcher:
use connectrpc_axum::prelude::*;
#[derive(Clone, Default)]
struct AppState;
// Tonic-compatible handler signatures (only these two compile):
// 1) (ConnectRequest<Req>)
// 2) (State<S>, ConnectRequest<Req>)
async fn say_hello(
State(_s): State<AppState>,
ConnectRequest(req): ConnectRequest<HelloRequest>,
) -> Result<ConnectResponse<HelloResponse>, ConnectError> {
Ok(ConnectResponse(HelloResponse { message: format!("Hello, {}!", req.name.unwrap_or_default()) }))
}
let (router, svc) = helloworldservice::HelloWorldServiceTonicCompatibleBuilder::new()
.say_hello(say_hello)
.with_state(AppState::default())
.build();
let grpc = hello_world_service_server::HelloWorldServiceServer::new(svc);
let dispatch = connectrpc_axum::ContentTypeSwitch::new(grpc, router);
axum::serve(listener, tower::make::Shared::new(dispatch)).await?;
Constraints in Tonic-compatible mode
(ConnectRequest<Req>) -> Result<ConnectResponse<Resp>, ConnectError>(State<S>, ConnectRequest<Req>) -> Result<ConnectResponse<Resp>, ConnectError>ConnectRequest<Req>.