Cannot merge two router's that both have a fallback.
If we give a little thought as to why that is, two services running at the same endpoint doesn't really make sense, does it? How would the program know which one to serve? The design saves you here from headaches down the line.
If you want to run both services, at least the port would need to be different. This would mean that you need to have two separate listeners for each port. Both listeners would be accompanied by their respective routers. The HTTP server can have the vitejs webapp as the fallback service and the gRPC router can have it's processor. Then we can spawn separate tokio tasks for them and both services would be able to be queried:
#[tokio::main] async fn main() { let t0 = tokio::task::spawn(async move { serve_grpc().await }); let t1 = tokio::task::spawn(async move { serve_http().await }); let _ = tokio::join!(t0, t1); } async fn serve_grpc() { let router = Router::new().fallback_service(get(|| async { "gRPC Processor" })); let listener = tokio::net::TcpListener::bind("127.0.0.1:5100") .await .unwrap(); axum::serve(listener, router).await.unwrap(); } async fn serve_http() { let serve_dir = ServeDir::new("web/dist"); let listener = tokio::net::TcpListener::bind("127.0.0.1:8080") .await .unwrap(); axum::serve( listener, Router::new() .fallback_service(serve_dir) .layer(CorsLayer::new().allow_origin(HeaderValue::from_static("self"))), ) .await .unwrap(); }
PS: Running both services via the same binary is not advisable as the concerns should be segregated but that is beyond the scope of this question.