2023-03-03 04:33:18 +00:00
# Axum Connect-Web
Brings the protobuf-based [Connect-Web RPC
framework](https://connect.build/docs/introduction) to Rust via idiomatic
2023-04-29 17:08:06 +00:00
[Axum ](https://github.com/tokio-rs/axum ).
# Alpha software ⚠️
Project is under active development for internal use, but minor revision bumps
are often breaking.
# Features 🔍
- Integrates into existing Axum HTTP applications seamlessly
- Closely mirrors Axum's API
- Extract State and other `parts` that impl `RpcFromRequestParts` just like
with Axum.
- Return any type that impl `RpcIntoResponse` just like Axum.
- Generated types and service handlers are strongly typed and...
- Handlers enforce semantically correct HTTP 'parts' access.
- Allows users to derive `RpcIntoResponse` and `RpcFromRequestParts` just like
Axum.
- Note: These must be derivatives of Axum's types because they are more
restrictive; you're not dealing with arbitrary HTTP any more, you're
speaking `connect-web` RPC **over** HTTP.
- Wrap `connect-web` error handling in idiomatic Axum/Rust.
- Codegen from `*.proto` files in a separate crate.
- All the other amazing benefits that come with Axum, like the community,
documentation and performance!
# Getting Started 🤓
2023-03-03 04:33:18 +00:00
_Prior knowledge with [Protobuf ](https://github.com/protocolbuffers/protobuf )
(both the IDL and it's use in RPC frameworks) and
[Axum ](https://github.com/tokio-rs/axum ) are assumed._
2023-04-29 17:08:06 +00:00
## Dependencies 🙄
Axum-connect uses [prost ](https://github.com/tokio-rs/prost ) for much of it's
protobuf manipulation. Prost sopped shipping `protoc` so you'll need to install
that manually, see the [grpc protoc install
instruction](https://grpc.io/docs/protoc-installation/). Alternatively there are
crates that ship pre-built protoc binaries.
```sh
# On Debian systems
sudo apt install protobuf-compiler
```
You'll need 2 axum-connect crates, one for code-gen and one for runtime use.
Because of how prost works, you'll also need to add it to your own project.
You'll obviously also need `axum` and `tokio` .
```sh
cargo add --build axum-connect-build
cargo add axum-connect prost axum
cargo add tokio --features full
```
## Protobuf File 🥱
Start by creating the obligatory 'hello world' proto service definition.
2023-03-03 04:33:18 +00:00
`proto/hello.proto`
```protobuf
syntax = "proto3";
2023-04-29 17:21:06 +00:00
package hello;
2023-03-03 04:33:18 +00:00
2023-04-29 17:21:06 +00:00
message HelloRequest { string name = 1; }
2023-03-03 04:33:18 +00:00
2023-04-29 17:21:06 +00:00
message HelloResponse { string message = 1; }
2023-03-03 04:33:18 +00:00
service HelloWorldService {
2023-04-29 17:21:06 +00:00
rpc SayHello(HelloRequest) returns (HelloResponse) {}
2023-03-03 04:33:18 +00:00
}
```
2023-04-29 17:08:06 +00:00
## Codegen 🤔
Use the `axum_connect_codegen` crate to generate Rust code from the proto IDL.
> Currently all codegen is done by having the proto files locally on-disk, and
> using a `build.rs` file. Someday I hope to support more of Buf's idioms like
> [Remote Code Gen](https://buf.build/docs/bsr/remote-plugins/usage).
2023-03-03 04:33:18 +00:00
`build.rs`
```rust
use axum_connect_build::axum_connect_codegen;
fn main() {
2023-04-29 17:08:06 +00:00
axum_connect_codegen(& ["proto"], &["proto/hello.proto"]).unwrap();
2023-03-03 04:33:18 +00:00
}
```
2023-04-29 17:08:06 +00:00
## The Fun Part 😁
With the boring stuff out of the way, let's implement out service using Axum!
2023-03-03 04:33:18 +00:00
```rust
use std::net::SocketAddr;
use axum::{extract::Host, Router};
2023-04-29 17:08:06 +00:00
use axum_connect::prelude::*;
use proto::hello::*;
2023-03-03 04:33:18 +00:00
mod proto {
2023-04-29 17:08:06 +00:00
pub mod hello {
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
}
2023-03-03 04:33:18 +00:00
}
#[tokio::main]
async fn main() {
// Build our application with a route. Note the `rpc` method which was added by `axum-connect` .
// It expect a service method handler, wrapped in it's respective type. The handler (below) is
// just a normal Rust function. Just like Axum, it also supports extractors!
2023-04-29 17:08:06 +00:00
let app = Router::new().rpc(HelloWorldService::say_hello(say_hello_success));
2023-03-03 04:33:18 +00:00
// Axum boilerplate to start the server.
2023-04-29 17:08:06 +00:00
let addr = SocketAddr::from(([127, 0, 0, 1], 3030));
2023-03-03 04:33:18 +00:00
println!("listening on http://{}", addr);
axum::Server::bind(& addr)
.serve(app.into_make_service())
.await
.unwrap();
}
2023-04-29 17:08:06 +00:00
async fn say_hello_success(Host(host): Host, request: HelloRequest) -> HelloResponse {
2023-03-03 04:33:18 +00:00
HelloResponse {
message: format!(
"Hello {}! You're addressing the hostname: {}.",
request.name, host
),
}
}
```
2023-04-29 17:08:06 +00:00
## SEND IT 🚀
To test it out, try hitting the endpoint manually.
```sh
curl --location 'http://localhost:3030/hello.HelloWorldService/SayHello' \
--header 'Content-Type: application/json' \
--data '{ "name": "Alec" }'
```
From here you can stand up a `connect-web` TypeScript/Go project to call your
API with end-to-end typed RPCs.
# Roadmap / Stated Non-Goals
- Binary proto encoding based on HTTP `content-type`
- Streaming server RPC responses
- Bring everything in-line with `connect-web`
- Version checking between generated and runtime code
- A plan for forward-compatibility
- Comprehensive tests
- A first-stable launch
## More Distant Goals
- I would love to also support a WASM-ready client library
- Use `buf.build` to support remote codegen and streamlined proto handling
- Support gRPC calls
- I don't think this is hard to do, I just have no personal use-case for it
- Possibly maybe-someday support BiDi streaming over WebRTC
- This would require `connect-web` picking up support for the same
- WebRTC streams because they are DTLS/SRTP and are resilient
- Replace Prost
## Non-goals
- To support every feature gRPC does
- You get a lot of this already with Axum, but gRPC is a monster that I
don't wish to reproduce. That complexity is useful for Google, and gets in
the way for pretty much everyone else.
- To do everything and integrate with everything
- I plan on keeping `axum-connect` highly focused. Good at what it does and
nothing else.
- This is idiomatic Rust. Do one thing well, and leave the rest to other
crates.
# Versioning
`axum-connect` and `axum-connect-build` versions are currently **not** kept in
lockstep. They will be once I get to beta. Right now the versions mean nothing
more than 'Alec pushed a new change'.
# License 🧾
2023-03-03 04:33:18 +00:00
Axum-Connect is dual licensed (at your option)
- MIT License ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT ](http://opensource.org/licenses/MIT ))
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0 ](http://www.apache.org/licenses/LICENSE-2.0 ))