Enables gRPC clients to work inside wasmCloud components using the standard wasi:http/outgoing-handler interface.
wasmCloud is a fast, secure WebAssembly framework for distributed applications. This project extends wasmCloud's capabilities by enabling components to make outbound gRPC requests, bridging the gap between Wasm sandboxing and modern microservice communication.
- gRPC over HTTP/2 support inside wasmCloud components
- Compatible with the wasmCloud security model via WASI interfaces
- Built with
tonicand the standardwasi:httpinterface - Works with most standard gRPC services
- Great for calling internal microservices or public gRPC APIs from wasmCloud components
- Automatic HTTP/2 connection pooling via the wasmCloud runtime
[dependencies]
anyhow = "1"
wasmcloud-component = "0.x" # wasmCloud component SDK
wasmcloud-grpc-client = "0.1.0"
tonic = { version = "0.12", default-features = false }
prost = "0.13"
[build-dependencies]
tonic-build = { version = "0.12", features = ["prost"] }In build.rs:
fn main() {
tonic_build::configure()
.build_transport(false) // Important: disable default transport
.compile_protos(&["proto/helloworld.proto"], &["proto"])
.unwrap();
}In your wasmcloud.toml:
name = "my-grpc-client"
language = "rust"
type = "component"
[component]
# Grant permission to make outbound HTTP requests
wit_world = "http-client"use tonic::Request;
use wasmcloud_grpc_client::GrpcEndpoint;
use hello_world::greeter_client::GreeterClient;
use hello_world::HelloRequest;
pub mod hello_world {
tonic::include_proto!("helloworld");
}
struct Component;
impl wasmcloud_component::http::Server for Component {
fn handle(
_req: wasmcloud_component::http::IncomingRequest,
) -> wasmcloud_component::http::Result<
wasmcloud_component::http::Response<impl wasmcloud_component::http::OutgoingBody>
> {
// Use tokio to run the async code in a blocking context
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| wasmcloud_component::http::ErrorCode::InternalError(
Some(format!("failed to create tokio runtime: {}", e))
))?;
runtime.block_on(async {
eprintln!("Starting gRPC client...");
// Parse the gRPC endpoint URI from config or use default
let endpoint_uri = std::env::var("GRPC_SERVER_URI")
.unwrap_or_else(|_| "http://[::1]:50051".to_string());
eprintln!("Connecting to gRPC server: {}", endpoint_uri);
let endpoint_uri = endpoint_uri
.parse()
.map_err(|e| wasmcloud_component::http::ErrorCode::InternalError(
Some(format!("failed to parse endpoint URI: {}", e))
))?;
// Create the gRPC endpoint wrapper
let endpoint = GrpcEndpoint::new(endpoint_uri);
// Create the gRPC client
let mut client = GreeterClient::new(endpoint);
// Make the gRPC call
let request = Request::new(HelloRequest {
name: "wasmCloud".to_string(),
});
eprintln!("Sending gRPC request...");
let response = client
.say_hello(request)
.await
.map_err(|e| wasmcloud_component::http::ErrorCode::InternalError(
Some(format!("gRPC call failed: {}", e))
))?;
let message = response.into_inner().message;
eprintln!("gRPC Response: {}", message);
Ok(wasmcloud_component::http::Response::new(message))
})
}
}
wasmcloud_component::http::export!(Component);┌─────────────────────────────────────┐
│ Your Component (WebAssembly) │
│ │
│ GreeterClient::say_hello() │
└──────────────┬──────────────────────┘
│ (tonic generates this)
↓
┌─────────────────────────────────────┐
│ GrpcEndpoint (Tower Service) │
│ - Converts hyper → WASI types │
│ - Calls wasi:http/outgoing-handler│
└──────────────┬──────────────────────┘
│ (WASI interface boundary)
↓
┌─────────────────────────────────────┐
│ wasmCloud Runtime (Host) │
│ - HTTP/2 connection pooling │
│ - TLS with ALPN negotiation │
│ - Actual network I/O │
└─────────────────────────────────────┘
The GrpcEndpoint acts as a bridge between tonic's hyper-based transport and wasmCloud's wasi:http/outgoing-handler interface. This allows gRPC clients to work seamlessly inside WebAssembly components while the wasmCloud runtime handles connection management, HTTP/2 multiplexing, and TLS.
wasmCloud components run in a secure sandbox with capability-based security. To make outbound gRPC requests:
- Your component must import the
wasi:http/outgoing-handlerinterface - The wasmCloud runtime enforces network access policies
- Connection pooling and TLS are handled securely by the host
This provides strong isolation between components while enabling controlled access to external services.
The wasmCloud runtime automatically pools HTTP/2 connections for you:
- Automatic multiplexing: Multiple gRPC calls reuse the same connection
- Transparent to components: No manual connection management needed
- Efficient resource usage: Connections are pooled per endpoint
- ALPN negotiation: Automatically selects HTTP/2 for HTTPS endpoints
For secure gRPC (gRPC over HTTPS):
// Just use https:// in the URI - TLS is handled automatically
let endpoint_uri = "https://grpc.example.com:443".parse()?;
let endpoint = GrpcEndpoint::new(endpoint_uri);
let mut client = MyServiceClient::new(endpoint);The wasmCloud runtime handles:
- TLS handshake and certificate verification
- ALPN negotiation (prefers HTTP/2)
- Connection security
| Feature | wasmCloud | Spin |
|---|---|---|
| Interface | wasi:http/outgoing-handler |
Custom Spin SDK |
| Runtime | wasmCloud (lattice-capable) | Spin runtime |
| Connection Pooling | Automatic (HTTP/2) | Automatic (HTTP/2) |
| TLS | Built-in with ALPN | Built-in with ALPN |
| Standards | WASI standard interfaces | Spin-specific APIs |
TODO
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.