An open API service indexing awesome lists of open source software.

https://github.com/dicklesworthstone/fastapi_rust

Rust web framework inspired by Python's FastAPI: type-safe routing, zero-copy parsing, OpenAPI generation, and structured concurrency via asupersync
https://github.com/dicklesworthstone/fastapi_rust

api async openapi rust web-framework

Last synced: about 6 hours ago
JSON representation

Rust web framework inspired by Python's FastAPI: type-safe routing, zero-copy parsing, OpenAPI generation, and structured concurrency via asupersync

Awesome Lists containing this project

README

          

# fastapi_rust


fastapi_rust - High-performance Rust web framework with FastAPI-inspired ergonomics

**High-performance Rust web framework with FastAPI-inspired ergonomics**

*A Rust port inspired by [tiangolo/fastapi](https://github.com/tiangolo/fastapi) (Python), extended with [asupersync](https://github.com/Dicklesworthstone/asupersync) for structured concurrency, zero-copy parsing, and deterministic testing.*

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Rust](https://img.shields.io/badge/rust-1.85+-orange.svg)](https://www.rust-lang.org/)
[![Status](https://img.shields.io/badge/status-early%20development-yellow.svg)]()

*Type-safe routing | Zero-copy parsing | Structured concurrency | OpenAPI generation*


Quick Install

```bash
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/fastapi_rust/main/install.sh?$(date +%s)" | bash
```

**Or add to your project:**

```toml
# Cargo.toml
[dependencies]
fastapi = { git = "https://github.com/Dicklesworthstone/fastapi_rust.git" }
asupersync = { git = "https://github.com/Dicklesworthstone/asupersync.git" }
serde = { version = "1", features = ["derive"] }
```

Requires Rust 1.85+ (2024 edition). Co-developed with asupersync.


---

## TL;DR

**The Problem**: Rust web frameworks either sacrifice developer ergonomics for performance (raw `hyper`) or hide allocations behind layers of abstraction (Axum + Tower). None leverage structured concurrency for cancel-correct request handling, and most require a massive dependency tree.

**The Solution**: fastapi_rust brings FastAPI's intuitive, type-driven API design to Rust with zero-copy HTTP parsing, compile-time route validation, and first-class integration with [asupersync](https://github.com/Dicklesworthstone/asupersync) for structured concurrency and deterministic testing.

### Why fastapi_rust?

| Feature | What It Does |
|---------|--------------|
| **Zero-copy HTTP parsing** | Requests parsed directly from buffers; no allocations on fast paths |
| **Compile-time route validation** | Invalid routes fail at build time via proc macros, not at runtime |
| **Structured concurrency** | Request handlers run in regions; cancellation is automatic and correct |
| **Type-driven extractors** | Declare parameter types; framework extracts and validates automatically |
| **Dependency discipline** | No Tokio/Hyper/Tower/Axum; direct deps kept small with a bias toward removal |
| **Deterministic testing** | Lab runtime for reproducible concurrent request tests |
| **FastAPI-compatible errors** | Validation errors match FastAPI's JSON format exactly |

---

## Quick Example

```rust
use fastapi_rust::prelude::*;

#[derive(Serialize, Deserialize, JsonSchema)]
struct Item {
id: i64,
name: String,
price: f64,
}

#[get("/items/{id}")]
async fn get_item(cx: &Cx, id: Path) -> Json {
cx.checkpoint()?; // Cancellation-safe yield point

Json(Item {
id: id.0,
name: "Widget".into(),
price: 29.99,
})
}

#[post("/items")]
async fn create_item(cx: &Cx, item: Json) -> Response {
// Automatic JSON deserialization with validation
// Wrong Content-Type -> 415
// Parse error -> 422 with detailed location
// Payload too large -> 413
Response::created().json(&item.0)
}

#[get("/search")]
async fn search(
cx: &Cx,
q: Query, // ?q=...&limit=...
auth: Header>, // Optional auth header
) -> Result, HttpError> {
// All extraction happens automatically
// Wrong types -> compile error
// Missing required -> 422 response
}

fn main() {
let app = App::builder()
.title("My API")
.version("1.0.0")
.route_entry(get_item_route())
.route_entry(create_item_route())
.route_entry(search_route())
.middleware(RequestIdMiddleware::new())
.middleware(Cors::permissive())
.build();

// Run with asupersync
let rt = asupersync::runtime::RuntimeBuilder::current_thread()
.build()
.expect("runtime must build");
rt.block_on(async move {
serve(app, "0.0.0.0:8000")
.await
.expect("server must start");
});
}
```

---

## Design Philosophy

### 1. Extract Spec, Never Translate

We study FastAPI's behavior and ergonomics, then implement idiomatically in Rust. No line-by-line Python translation - Rust has better tools for these problems:

- Python decorators -> Rust procedural macros
- Pydantic validation -> Compile-time type checking + serde
- ASGI lifecycle -> Structured concurrency regions
- Runtime reflection -> Compile-time code generation

### 2. Zero-Cost Abstractions

| Technique | Implementation |
|-----------|----------------|
| No runtime reflection | Proc macros analyze types at compile time |
| No trait objects on hot paths | Monomorphization via generics |
| Pre-allocated buffers | 4KB default, configurable per-route |
| Zero-copy HTTP parsing | Borrowed types reference request buffer |
| Inline critical paths | `#[inline(always)]` on hot code |

### 3. Cancel-Correct by Default

Every request handler runs in an asupersync **region**. Client disconnects, timeouts, and shutdowns trigger graceful cancellation:

```
Connection Accepted
|
v
+-----------------------------------------------+
| Request Region (owns all request work) |
| +-------------------------------------------+|
| | Handler Task ||
| | +-- Dependency Task (DB query) ||
| | +-- Dependency Task (cache lookup) ||
| | +-- Background Task (logging) ||
| +-------------------------------------------+|
| |
| Region close waits for ALL tasks to finish |
+-----------------------------------------------+
|
v
Response Sent (only after region quiescent)
```

**No orphaned tasks.** Client disconnect -> cancel region -> all tasks cleaned up.

### 4. Dependency Discipline

| Crate | Purpose | Why |
|-------|---------|-----|
| `asupersync` | Async runtime | Our own - cancel-correct, capability-secure |
| `serde` | Serialization traits | Zero-cost, industry standard |
| `serde_json` | JSON parsing | Fast, well-optimized |

**Explicitly avoided:** Tokio, Hyper, Axum, Tower, and runtime-reflection/schema-building crates.

Note: Some workspace crates currently use additional small utility/test/proc-macro dependencies
where it meaningfully improves safety or developer experience. Shrinking this further is an active
goal tracked in Beads (see `bd-uz2s`).

---

## How fastapi_rust Compares

| Feature | fastapi_rust | Axum | Actix-web | Rocket |
|---------|--------------|------|-----------|--------|
| Zero-copy HTTP parsing | **Custom** | Hyper | Partial | No |
| Compile-time routes | **Proc macros** | Runtime | Runtime | Macros |
| Structured concurrency | **asupersync** | Tokio spawn | Actix-rt | Tokio |
| Cancel-correct shutdown | **Native** | Manual | Manual | Manual |
| Dependency injection | **Native + cache** | State only | Data only | Managed |
| OpenAPI generation | **Compile-time** | External | External | External |
| Deterministic testing | **Lab runtime** | No | No | No |
| Direct deps (core runtime crates) | **Low (single digits)** | ~80+ | ~60+ | ~50+ |
| FastAPI-style errors | **Yes (422 format)** | No | No | No |

### When to Use fastapi_rust

- You need cancel-correct request handling (graceful shutdown, timeouts)
- You want compile-time route validation
- You're building with asupersync for structured concurrency
- You want deterministic tests for concurrent code
- You're familiar with FastAPI and want similar ergonomics in Rust

### When to Consider Alternatives

- You need production-proven stability today (fastapi_rust is v0.1.2)
- You require production-hardened WebSocket support (basic support exists; full parity tracked in `bd-z09e`)
- You have existing Tokio-based infrastructure
- You need the massive ecosystem of Tower middleware

---

## Installation

### Add to Cargo.toml

```toml
[dependencies]
fastapi_rust = { package = "fastapi-rust", version = "0.1.2" }
asupersync = "0.1.1"
serde = { version = "1", features = ["derive"] }
```

**Note**: The crates.io package is `fastapi-rust`, and the crate name is `fastapi_rust`.

### From Source

```bash
git clone https://github.com/Dicklesworthstone/fastapi_rust.git
cd fastapi_rust
cargo build --release
```

### Requirements

- **Rust 1.85+** (2024 edition)
- **[asupersync](https://github.com/Dicklesworthstone/asupersync)** (co-developed runtime)

---

## Architecture

```
+-------------------------------------------------------------------+
| fastapi_rust (facade) |
| Re-exports all public types, prelude module |
+-------------------------------------------------------------------+
| | | | |
v v v v v
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
| core | | http | | router | | macros | | openapi |
| | | | | | | | | |
| - Request | | - Parser | | - Trie | | - #[get] | | - Schema |
| - Response| | - Body | | - Match | | - #[post] | | - Builder |
| - Context | | - Query | | - Registry| | - Derive | | - Spec |
| - Extract | | - Headers | | | | | | |
| - Depends | | - Writer | | | | | | |
| - Error | | - Server | | | | | | |
| - Middle. | | - Stream | | | | | | |
| - Logging | | | | | | | | |
| - Testing | | | | | | | | |
| - Shutdown| | | | | | | | |
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
|
v
+-------------------------------------------------------------------+
| asupersync |
| Structured concurrency - Cx - Regions - Budgets - Lab |
+-------------------------------------------------------------------+
```

### Crate Overview

| Crate | ~Lines | Purpose |
|-------|--------|---------|
| `fastapi_rust` | 100 | Facade: re-exports, prelude |
| `fastapi-core` | 6,000 | Request, Response, extractors, DI, middleware, logging, testing, shutdown |
| `fastapi-http` | 2,500 | Zero-copy HTTP/1.1 parser, body handling, query parsing, streaming |
| `fastapi-router` | 600 | Radix trie routing, path matching, conflict detection |
| `fastapi-macros` | 400 | `#[get]`, `#[post]`, `#[derive(Validate)]`, `#[derive(JsonSchema)]` |
| `fastapi-openapi` | 500 | OpenAPI 3.1 types, schema builder, spec generation |

---

## Extractors

Extract typed data from requests declaratively:

```rust
use fastapi_rust::prelude::*;

#[get("/users/{id}")]
async fn get_user(
cx: &Cx, // Capability context (required)
id: Path, // Path parameter: /users/123
q: Query, // Query string: ?q=...&limit=...
auth: Header, // Required header
accept: Header>, // Optional header
) -> Result, HttpError> {
// Types declare what you need - framework handles extraction
// Wrong types -> compile error
// Missing required -> 422 with FastAPI-compatible error
}

#[post("/items")]
async fn create(
cx: &Cx,
item: Json, // JSON body
) -> Result {
// 415 if wrong Content-Type
// 413 if payload too large (configurable)
// 422 if parse error (with location path)
}
```

### Available Extractors

| Extractor | Description | Error Response |
|-----------|-------------|----------------|
| `Path` | URL path parameters | 422 if missing/wrong type |
| `Query` | Query string | 422 if missing/invalid |
| `Json` | JSON request body | 415/413/422 |
| `Header` | Single header value | 422 if missing/invalid |
| `HeaderValues` | All values for header | 422 if invalid |
| `State` | Application state | 500 if not configured |
| `Depends` | Dependency injection | Depends on factory |
| `Option` | Any extractor, optional | Never fails |

---

## Middleware

Composable middleware with onion model execution:

```rust
use fastapi_rust::prelude::*;

// Built-in middleware
let app = App::builder()
.middleware(RequestIdMiddleware::new()) // Add X-Request-Id
.middleware(RequestResponseLogger::default()) // Log all requests
.middleware(Cors::permissive()) // CORS handling
.middleware(RequireHeader::new("X-API-Key")) // Require header
.middleware(AddResponseHeader::new("X-Powered-By", b"fastapi_rust"))
.build();

// Custom middleware
struct Timing;

impl Middleware for Timing {
async fn before(&self, ctx: &RequestContext, req: &mut Request) -> ControlFlow {
// Store start time in context
ControlFlow::Continue
}

async fn after(&self, ctx: &RequestContext, req: &Request, mut resp: Response) -> Response {
// Add X-Response-Time header
resp
}
}
```

### Execution Order

```
Request -> MW1.before -> MW2.before -> MW3.before -> Handler
|
Response <- MW1.after <- MW2.after <- MW3.after <- Response
```

First registered runs first on the way in, last on the way out (onion model).

---

## Dependency Injection

Request-scoped dependencies with caching:

```rust
use fastapi_rust::prelude::*;

// Define a dependency
#[derive(Clone)]
struct DatabasePool { /* ... */ }

impl FromDependency for DatabasePool {
type Error = HttpError;

async fn from_dependency(ctx: &RequestContext, req: &mut Request) -> Result {
// Resolved once per request, cached for subsequent uses
Ok(DatabasePool::connect().await?)
}
}

// Use in handler
#[get("/users/{id}")]
async fn get_user(
cx: &Cx,
id: Path,
db: Depends, // Automatically resolved and cached
) -> Result, HttpError> {
let user = db.fetch_user(id.0).await?;
Ok(Json(user))
}

// Override for testing
let overrides = DependencyOverrides::new()
.with::(MockDatabase::new());
```

### Dependency Scopes

| Scope | Behavior |
|-------|----------|
| `Request` (default) | Resolve once, cache for request lifetime |
| `Function` | Resolve on every extraction |
| `NoCache` | Explicit opt-out of caching |

---

## Testing

In-process testing without network I/O:

```rust
use fastapi_rust::testing::*;

#[test]
fn test_get_item() {
let client = TestClient::new(app);

let resp = client.get("/items/42")
.header("Authorization", "Bearer token")
.send();

assert_eq!(resp.status(), 200);

let item: Item = resp.json();
assert_eq!(item.id, 42);
}

#[test]
fn test_deterministic() {
// Same seed = same execution order for concurrent operations
let client = TestClient::with_seed(app, 12345);

// Reproducible even with concurrent handlers
let resp = client.post("/items")
.json(&new_item)
.send();

assert_eq!(resp.status(), 201);
}

#[test]
fn test_with_overrides() {
let overrides = DependencyOverrides::new()
.with::(MockDatabase::new());

let client = TestClient::new(app)
.with_overrides(overrides);

// Handler receives MockDatabase instead of real one
}
```

### Assertion Helpers

```rust
use fastapi_core::{assert_status, assert_header, assert_json};

assert_status!(resp, 200);
assert_header!(resp, "Content-Type", "application/json");
assert_json!(resp, {"id": 42, "name": "Widget"});
```

---

## Error Handling

FastAPI-compatible validation errors:

```rust
// Handler returns HttpError
#[get("/items/{id}")]
async fn get_item(id: Path) -> Result, HttpError> {
if id.0 < 0 {
return Err(HttpError::unprocessable_entity()
.detail("ID must be positive")
.loc(["path", "id"]));
}
// ...
}
```

**Error response format (FastAPI-compatible):**

```json
{
"detail": [
{
"type": "value_error",
"loc": ["path", "id"],
"msg": "ID must be positive",
"input": -1
}
]
}
```

### Built-in Error Types

| Status | Constructor | Use Case |
|--------|-------------|----------|
| 400 | `HttpError::bad_request()` | Malformed request |
| 401 | `HttpError::unauthorized()` | Missing/invalid auth |
| 403 | `HttpError::forbidden()` | Permission denied |
| 404 | `HttpError::not_found()` | Resource not found |
| 413 | `HttpError::payload_too_large()` | Body exceeds limit |
| 415 | `HttpError::unsupported_media_type()` | Wrong Content-Type |
| 422 | `HttpError::unprocessable_entity()` | Validation failed |
| 500 | `HttpError::internal()` | Server error |

---

## Graceful Shutdown

Cancel-correct shutdown with configurable grace periods:

```rust
use fastapi_rust::prelude::*;
use fastapi_core::shutdown::*;

let app = App::builder()
.graceful_shutdown(GracefulConfig {
grace_period: Duration::from_secs(30),
force_timeout: Duration::from_secs(5),
})
.on_shutdown(|phase| async move {
match phase {
ShutdownPhase::GracePeriod => {
// Stop accepting new connections
// Wait for in-flight requests
}
ShutdownPhase::ForceClose => {
// Cancel remaining requests
}
}
Ok(())
})
.build();
```

Shutdown propagates through asupersync regions - no orphaned tasks.

---

## Configuration

```rust
use fastapi_rust::prelude::*;

let app = App::builder()
// Metadata
.title("My API")
.version("1.0.0")
.description("A sample API built with fastapi_rust")

// Routes
.route_entry(get_item_route())
.route_entry(create_item_route())
.route_entry(delete_item_route())

// Middleware (order matters)
.middleware(RequestIdMiddleware::new())
.middleware(Cors::new(CorsConfig {
allow_origins: vec!["https://example.com".into()],
allow_methods: vec![Method::Get, Method::Post],
allow_headers: vec!["Authorization".into()],
max_age: Some(3600),
}))

// Shared state
.state(DatabasePool::new())
.state(CacheClient::new())

// Exception handlers
.exception_handler(|err: DatabaseError| {
HttpError::internal().detail(err.to_string())
})

// Lifecycle hooks
.on_startup(|| async {
println!("Starting up...");
Ok(())
})
.on_shutdown(|_| async {
println!("Shutting down...");
Ok(())
})

// Build
.build();
```

---

## Troubleshooting

### Common Issues

| Problem | Cause | Solution |
|---------|-------|----------|
| `asupersync not found` | Missing dependency | Add `asupersync` to Cargo.toml |
| `Cx lifetime error` | Holding Cx across await | Use `cx.checkpoint()` pattern |
| Route conflicts | Overlapping path patterns | Check for `{param}` vs literal conflicts |
| 422 on valid JSON | Missing `#[derive(Deserialize)]` | Add serde derive to your types |
| Middleware not running | Wrong registration order | Check middleware ordering |

### Debugging Tips

```rust
// Enable request logging
.middleware(RequestResponseLogger::new(LogConfig {
log_bodies: true,
log_headers: true,
}))

// Check route registration
app.routes().for_each(|r| println!("{} {}", r.method, r.path));

// Deterministic test reproduction
TestClient::with_seed(app, failing_seed)
```

---

## Parity Status (Goal: 100% FastAPI Coverage)

This project is intended to reach **100% feature-for-feature, behavior-for-behavior parity** with
the legacy Python FastAPI library.

Current parity status and the concrete gap list are tracked in:
- `PROPOSED_RUST_ARCHITECTURE.md` (Section 0: Parity Matrix)
- Beads (`br ready`, `br show `) with the top-level epic `bd-uz2s`

### Known Gaps (As Of 2026-02-11)

- **OpenAPI generation**: currently minimal; needs real operation/schema mapping from route metadata.
- **TCP server integration/hardening**: `fastapi-http` has a server implementation, but the end-to-end
surface is still evolving.
- **WebSockets**: partial (handshake + basic frames). Full FastAPI/Starlette parity is tracked in `bd-z09e`.
- **Multipart/form-data + file uploads**: parser + `MultipartForm` extractor + incremental streamed-body parsing +
streamed-part incremental flushing + spool-backed file parts + `UploadFile` async API (`read`/`write`/`seek`/`close`) are implemented.
Remaining parity work in `bd-3ess` is mainly API-surface refinement around fully streamed consumption paths and broader behavior parity edge cases.
- **HTTP/2**: missing (`bd-2c9t`).

### Non-Negotiables / Constraints

- **Requires asupersync**: no Tokio support (by design).
- **Rust 1.85+**: uses Rust 2024 edition features.
- **Early development**: API will change before v1.0.

---

## FAQ

### Why "fastapi_rust"?

It's a Rust web framework inspired by Python's [FastAPI](https://fastapi.tiangolo.com/), preserving the type-driven API design while achieving native performance and cancel-correctness.

### Why not use Tokio/Axum?

Tokio's spawn model makes cancel-correctness difficult - tasks can outlive their scope, leading to resource leaks and subtle bugs. asupersync's structured concurrency ensures all request-related work completes or cancels together.

### Can I use this in production?

Not production-ready yet. This is v0.1.2 in active development; the TCP server exists (built on `asupersync::net`),
but parity and production hardening are tracked under `bd-uz2s`.

### How fast is it?

We haven't published end-to-end benchmarks yet, but the architecture is designed for:
- Zero allocations on the fast path
- Zero-copy request parsing
- No runtime reflection
- Pre-allocated buffers (4KB default)

### Does it support async/await?

Yes, fully. All handlers, middleware, and extractors are async-native, built on asupersync's structured concurrency model.

### Why the minimal dependency approach?

Each dependency is a maintenance burden, security surface, and compile-time cost. By keeping dependencies small and intentional, we:
- Reduce build times significantly
- Have full control over behavior
- Avoid dependency conflicts
- Make auditing practical

### How do validation errors compare to FastAPI?

Identical format. The same client code that handles FastAPI 422 responses will work with fastapi_rust:

```json
{
"detail": [
{"type": "missing", "loc": ["body", "email"], "msg": "Field required"}
]
}
```

---

## About Contributions

Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.

---

## License

MIT license. See [LICENSE](LICENSE).

---

## Related Projects

| Project | Description |
|---------|-------------|
| [asupersync](https://github.com/Dicklesworthstone/asupersync) | Structured concurrency async runtime (co-developed) |
| [FastAPI](https://fastapi.tiangolo.com/) | The Python framework that inspired this project |