Skip to content

Latest commit

 

History

History
255 lines (185 loc) · 6.24 KB

File metadata and controls

255 lines (185 loc) · 6.24 KB

RustAPI Philosophy

"The power of Rust. Modern DX. LLM-ready."


The Core Idea

"API surface is ours, engines can change."

This single principle drives every architectural decision in RustAPI. You write code against a stable, ergonomic API. We handle the complexity of HTTP protocols, async runtimes, and serialization libraries internally.


Why We Built RustAPI

The Problem

Building APIs in Rust traditionally requires:

  • Understanding hyper's low-level HTTP primitives
  • Managing tokio runtime configurations
  • Fighting trait bounds like Pin<Box<dyn Future<...>>>
  • Writing boilerplate for request parsing, validation, and error handling
  • Manually documenting every endpoint

The result? Simple APIs take 100+ lines. Developers spend more time on plumbing than business logic.

The Solution

RustAPI provides a facade — a clean, stable API that wraps all the complexity:

// This is all you need. No boilerplate.
use rustapi_rs::prelude::*;

#[rustapi_rs::get("/users/{id}")]
async fn get_user(Path(id): Path<u64>) -> Json<User> {
    Json(User::find(id).await)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    RustApi::auto().run("0.0.0.0:8080").await
}

5 lines. Auto-generated OpenAPI. Production-ready.


Design Principles

1. 🎯 5-Line APIs

Every feature must be expressible in minimal code.

Framework Hello World Lines
Raw Hyper ~50 lines
Axum ~15 lines
RustAPI 5 lines

We achieve this through:

  • Sensible defaults (auto-route registration, built-in Swagger UI)
  • Derive macros that eliminate boilerplate
  • A prelude that exports everything you need

2. 🛡️ Stable API Surface

Your code depends only on rustapi-rs. Internal dependencies are hidden.

# Your Cargo.toml - simple and stable
[dependencies]
rustapi-rs = "0.1.335"

You never write:

# ❌ Not this - internal details exposed
hyper = "1.0"
tokio = "1.35"
validator = "0.16"

Benefits:

  • No dependency conflicts
  • Simpler Cargo.toml
  • We can upgrade internals without breaking your code

3. 🔄 Engines Can Change

The facade pattern lets us swap implementations freely.

Component Current Engine Could Become
HTTP Server hyper 1.x hyper 2.x, h3 (HTTP/3)
Async Runtime tokio smol, async-std (future)
Validation Native RustAPI v2 (legacy optional) Further optimized native engine
Router matchit Custom radix tree
OpenAPI Native implementation Further optimized native implementation

Example scenario: When hyper 2.0 releases with breaking changes:

  1. We update rustapi-core to use hyper 2.0
  2. We bump rustapi-rs to 0.2.0
  3. Your code stays exactly the same — just update the version

4. 🎁 Batteries Included (But Optional)

Everything you need, nothing you don't.

# Just the basics
rustapi-rs = "0.1.335"

# Kitchen sink
rustapi-rs = { version = "0.1.335", features = ["full"] }

# Pick what you need
rustapi-rs = { version = "0.1.335", features = ["extras-jwt", "extras-cors", "protocol-toon"] }
Feature What You Get
extras-jwt JWT authentication with AuthUser<T> extractor
extras-cors CORS middleware with builder pattern
extras-rate-limit IP-based rate limiting
protocol-toon LLM-optimized TOON format
core-openapi Auto-generated /docs endpoint
full All features enabled

5. 🤖 LLM-First Design

Built for the AI era.

Traditional JSON:

{"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}],"total":2}

~20 tokens

TOON format:

users[(id:1,name:Alice)(id:2,name:Bob)]total:2

~9 tokens (55% savings)

RustAPI provides:

  • Toon<T> — Direct TOON responses
  • LlmResponse<T> — Content negotiation with token counting headers
  • AcceptHeader — Automatic format detection
#[rustapi_rs::get("/ai/data")]
async fn ai_endpoint(accept: AcceptHeader) -> LlmResponse<Data> {
    LlmResponse::new(data, accept.preferred)
}
// Response headers: X-Token-Count-JSON, X-Token-Count-TOON, X-Token-Savings

What We DON'T Do

No Direct Dependency Exposure

// ❌ We don't expose hyper types
fn handler(req: hyper::Request<...>) -> hyper::Response<...>

// ✅ We provide our own abstractions
fn handler(req: Request) -> impl IntoResponse

No Configuration Hell

// ❌ Not this
let server = Server::builder()
    .http1_header_max_size(8192)
    .http1_only(true)
    .tcp_keepalive(Some(Duration::from_secs(60)))
    .build(...);

// ✅ This
RustApi::new().run("0.0.0.0:8080").await

No Trait Bound Nightmares

// ❌ Not this
where
    T: Service<Request<Body>, Response = Response<ResBody>> + Clone + Send + 'static,
    T::Error: Into<BoxError>,
    T::Future: Send,
    ResBody: Body<Data = Bytes> + Send + 'static,
    ResBody::Error: Into<BoxError>,

// ✅ Just this
async fn handler(Json(body): Json<T>) -> Json<R>

Success Metrics

Goal Target Achieved
Hello World ≤ 5 lines ✅ 5 lines
CRUD tutorial ≤ 15 min ✅ ~10 min
First Swagger UI Zero config ✅ Auto at /docs
Compile errors Understandable ✅ Clear hints
LLM token savings ≥ 50% ✅ 50-58%

The Path Forward

Short Term (v0.x)

  • Polish existing features
  • Performance optimizations (core-simd-json, better allocations)
  • More middleware (compression, static files)

Medium Term (v1.0)

  • Custom validation engine (remove validator dependency)
  • Async validation support
  • Stable API guarantee

Long Term

  • WebSocket support
  • GraphQL (optional crate)
  • gRPC (optional crate)
  • HTTP/3 via h3 (transparent upgrade)

Summary

RustAPI is not just another web framework. It's a philosophy:

  1. Simplicity first — 5 lines to production
  2. Stability always — Your code never breaks
  3. Future-ready — Built for AI, ready for anything
use rustapi_rs::prelude::*;

// This will work in 2024, 2025, and beyond.
// Engines change. Your code doesn't.

"API surface is ours, engines can change."