| Crates.io | at-jet |
| lib.rs | at-jet |
| version | 0.4.0 |
| created_at | 2026-01-09 14:35:04.160736+00 |
| updated_at | 2026-01-15 06:48:17.591218+00 |
| description | High-performance HTTP + Protobuf API framework for mobile services |
| homepage | |
| repository | |
| max_upload_size | |
| id | 2032191 |
| size | 190,903 |
High-performance HTTP + Protobuf API framework for mobile services.
Not just a wrapper - AT-Jet is an opinionated, production-ready foundation for mobile API development. See Architecture Rationale for why this project exists.
Building mobile APIs with Protobuf over HTTP requires solving the same problems repeatedly:
Without AT-Jet (30+ lines per handler):
async fn create_user(headers: HeaderMap, body: Bytes) -> impl IntoResponse {
// Check content-type... validate size... decode proto... handle errors...
}
With AT-Jet (5 lines):
async fn create_user(ProtobufRequest(req): ProtobufRequest<CreateUserRequest>) -> ProtobufResponse<User> {
ProtobufResponse::ok(User { id: 1, name: req.name })
}
Add to your Cargo.toml:
[dependencies]
at-jet = "0.3"
tokio = { version = "1", features = ["full"] }
prost = "0.13"
[build-dependencies]
prost-build = "0.13"
See Quick Start Guide for a complete 5-minute tutorial.
use at_jet::prelude::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let server = JetServer::new()
.route("/api/users", get(list_users).post(create_user))
.route("/api/users/:id", get(get_user))
.with_cors()
.with_compression();
server.serve("0.0.0.0:8080").await?;
Ok(())
}
async fn get_user(Path(id): Path<i32>) -> ProtobufResponse<User> {
let user = User { id, name: "John".to_string() };
ProtobufResponse::ok(user)
}
async fn list_users() -> ProtobufResponse<ListUsersResponse> {
// Return list of users
ProtobufResponse::ok(response)
}
async fn create_user(
ProtobufRequest(req): ProtobufRequest<CreateUserRequest>
) -> ProtobufResponse<User> {
// Create user from request
ProtobufResponse::created(user)
}
use at_jet::prelude::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Protobuf client (production)
let client = JetClient::new("https://api.example.com")?;
// GET request
let user: User = client.get("/api/users/123").await?;
// POST request
let request = CreateUserRequest { name: "John".to_string() };
let created: User = client.post("/api/users", &request).await?;
// JSON debug client (for development)
let debug_client = JetClient::builder()
.base_url("https://api.example.com")
.debug_key("dev-debug-key")
.build()?;
// JSON requests for debugging
let user_json: UserJson = debug_client.get_json("/api/users/123").await?;
let raw_json = debug_client.get_json_raw("/api/users").await?;
println!("Response: {}", raw_json);
Ok(())
}
AT-Jet supports both Protobuf (production) and JSON (debugging) formats. JSON requires authorization via debug keys to prevent accidental use in production.
Protobuf provides schema evolution guarantees that JSON lacks:
If clients accidentally use JSON in production, they lose these guarantees and may break when the schema evolves. The debug key requirement ensures JSON is only used intentionally.
use at_jet::prelude::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Configure authorized debug keys (call once at startup)
configure_debug_keys(vec![
"alice-dev-key".to_string(),
"bob-dev-key".to_string(),
"qa-team-key".to_string(),
]);
// Or disable JSON completely (production default)
// configure_debug_keys(vec![]);
let server = JetServer::new()
.route("/api/users", post(create_user))
.with_cors();
server.serve("0.0.0.0:8080").await?;
Ok(())
}
use at_jet::prelude::*;
// Dual-format handler - accepts both Protobuf and JSON
async fn create_user(
ApiRequest { body, format }: ApiRequest<CreateUserRequest>
) -> ApiResponse<User> {
let user = User { id: 1, name: body.name };
ApiResponse::ok(format, user) // Response format matches Accept header
}
# Protobuf request (production - no debug key needed)
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/x-protobuf" \
-H "Accept: application/x-protobuf" \
--data-binary @request.pb
# JSON request (debugging - requires valid debug key)
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-Debug-Format: alice-dev-key" \
-d '{"name": "John"}'
# JSON request without debug key → rejected with 415 Unsupported Media Type
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name": "John"}'
| Request Format | Response Format | Condition |
|---|---|---|
| Protobuf | Protobuf | Default (production) |
| JSON | JSON | Valid X-Debug-Format header |
| JSON | Error 415 | Missing/invalid debug key |
| Protobuf | JSON | Accept: application/json + valid debug key |
Mobile Clients (iOS, Android, Web)
│
▼
CDN (Global)
│
▼
┌─────────────┐
│ AT-Jet │ ◄── HTTP + Protobuf
│ Server │
└─────────────┘
│
▼
┌─────────────┐
│ Backend │ ◄── ZUS-RS (internal RPC)
│ Services │
└─────────────┘
| Factor | JSON | Protobuf |
|---|---|---|
| Size | 100% | 30-40% (60-70% smaller) |
| Parse Speed | 100% | 10-20% (5-10x faster) |
| Schema Evolution | Manual | Built-in |
| Type Safety | Runtime | Compile-time |
# Start server
cargo run --example basic_server
# In another terminal, run client
cargo run --example basic_client
MIT OR Apache-2.0