- High Speed: Powered by Rust and Axum, FastrAPI delivers up to 6x faster performance than FastAPI, making your APIs scream.
- Python First: Write same Python code, 0 Rust knowledge needed. FastrAPI handles the heavy lifting behind the scenes.
- Pydantic Powered: Seamless integration with Pydantic for effortless request and response validation, keeping your data in check.
- Async Native: Built on Tokio's async runtime, FastrAPI maximizes concurrency for handling thousands of requests with ease.
- Ultra Lightweight: Minimal runtime overhead with maximum throughput.
- Drop in Replacement: Drop in compatibility with the same FastAPI's beloved decorator syntax, so you can switch without rewriting your codebase.
- Middleware Support:
tower-httpsupport for CORS, GZip, Session, and TrustedHost middleware.
Yes. Powered by Rust and Axum, FastrAPI outperforms FastAPI by up to 6x in real-world benchmarks, with no compromises on usability. Check it out here
Nope. FastrAPI lets you write 100% Python code while still leveraging Rust's performance under the hood.
Absolutely, FastrAPI scales effortlessly for small projects and massive enterprise grade APIs alike.
Yes. FastrAPI mirrors FastAPI's syntax, ensuring compatibility and instant access to workflows.
uv install fastrapipip install fastrapimaturin build --release --pgo --generate-stubs- from fastapi import FastAPI
+ from fastrapi import FastrAPIfrom fastrapi import FastrAPI
app = FastrAPI()
@app.get("/hello")
def hello():
return {"Hello": "World"}
@app.get("/healthz", cache_resp=True)
def healthz():
return {"ok": True}
@app.post("/echo")
def echo(data):
return {"received": data}
if __name__ == "__main__":
app.serve("127.0.0.1", 8000)curl http://127.0.0.1:8000/helloFor the POST endpoint:
curl --location 'http://127.0.0.1:8000/echo' \
--header 'Content-Type: application/json' \
--data '{"foo": 123, "bar": [1, 2, 3]}'Show Pydantic example
from pydantic import BaseModel
from fastrapi import FastrAPI
api = FastrAPI()
class User(BaseModel):
name: str
age: int
@api.post("/create_user")
def create_user(data: User):
return {"msg": f"Hello {data.name}, age {data.age}"}
api.serve("127.0.0.1", 8000)Show ResponseTypes Example
from fastrapi import FastrAPI
from fastrapi.responses import HTMLResponse, JSONResponse
api = FastrAPI()
@api.get("/html")
def get_html() -> HTMLResponse:
return HTMLResponse("<h1>Hello</h1>")
api.serve("127.0.0.1", 8000)Show Middleware Example
from fastrapi import FastrAPI
from fastrapi.responses import JSONResponse
from fastrapi.middleware import (
CORSMiddleware,
TrustedHostMiddleware,
GZipMiddleware,
SessionMiddleware
)
app = FastrAPI()
# TrustedHost Middleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["127.0.0.1", "localhost", "127.0.0.1:8000"],
www_redirect=True
)
# CORS Middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["GET", "POST"],
allow_headers=["*"],
allow_credentials=False
)
# 3. GZip Middleware
app.add_middleware(
GZipMiddleware,
minimum_size=500,
compresslevel=9
)
# 4. Session Middleware
app.add_middleware(
SessionMiddleware,
secret_key="super-duper-secret-key-change-this-in-prod-pwease-uwu-BUT-MAKE-IT-LONGER-NOW",
session_cookie="fastrapi_session",
max_age=3600,
https_only=False
)
# ROUTES
@app.get("/")
def index() -> JSONResponse:
return JSONResponse({"status": "running"})
@app.get("/heavy")
def heavy_data() -> JSONResponse:
# response large enough to trigger GZip compression
large_data = "x" * 1000
return JSONResponse({
"data": large_data,
"note": "Check content-encoding header!"
})
# Session Test: Increment a counter stored in the cookie
@app.get("/counter")
def session_counter(request) -> JSONResponse:
# For now, this verifies the Middleware sets the cookie correctly.
return JSONResponse({"message": "Session cookie should be set"})
if __name__ == "__main__":
app.serve("127.0.0.1", 8000)
# Test with:
# curl -v -H "Host: 127.0.0.1" http://127.0.0.1:8000/
# curl -v -H "Origin: http://example.com" http://127.0.0.1:8000/Show Lifespan Example
from contextlib import asynccontextmanager
from fastrapi import FastrAPI
shared = {}
@asynccontextmanager
async def lifespan(app: FastrAPI):
shared["ready"] = True
app.title = "FastrAPI && lifespan"
try:
yield
finally:
shared.clear()
app = FastrAPI(lifespan=lifespan)
@app.get("/health")
def health():
return {"ready": shared.get("ready", False), "title": app.title}
app.serve("127.0.0.1", 8080)If you provide lifespan=..., on_startup and on_shutdown handlers are not called.
Show Startup / Shutdown Example
from fastrapi import FastrAPI
events = []
def startup():
events.append("startup")
async def shutdown():
events.append("shutdown")
app = FastrAPI(
on_startup=[startup],
on_shutdown=[shutdown],
)
@app.get("/events")
def get_events():
return {"events": events}
app.serve("127.0.0.1", 8080)Use cache_resp=True only for immutable responses. FastrAPI calls the handler during startup, stores the rendered response bytes and headers, and serves that route through a no-Python Axum path.
@app.get("/", cache_resp=True)
def hello():
return {"Hello": "World"}Benchmarks using k6 show it outperforms FastAPI + Guvicorn across multiple worker configurations.
For real benchmark numbers, build the PyO3 extension in release mode first:
maturin develop --release
python examples/basic.py
k6 run benchmarks/stress.jsIf you benchmark a debug build, Rust-side overhead will be much higher and the numbers will be misleading.
- Kernel: 6.16.8-arch3-1
- CPU: AMD Ryzen 7 7735HS (16 cores, 4.83 GHz)
- Memory: 15 GB
- Load Test: 20 Virtual Users (VUs), 30s
| Framework | Avg Latency (ms) | Median Latency (ms) | Requests/sec | P95 Latency (ms) | P99 Latency (ms) |
|---|---|---|---|---|---|
| FASTRAPI | 0.59 | 0.00 | 31360 | 2.39 | 11.12 |
| FastAPI + Guvicorn (workers: 1) | 21.08 | 19.67 | 937 | 38.47 | 93.42 |
| FastAPI + Guvicorn (workers: 16) | 4.84 | 4.17 | 3882 | 10.22 | 81.20 |
TLDR; FASTRAPI can handle thousands of requests per second with ultra-low latency , making it ~6× faster than FastAPI + Guvicorn.
| Area | FastAPI | FastRAPI | FastRAPI wins? |
|---|---|---|---|
| Dependency resolution | Runtime inspect + reflection every request |
One time parsing at startup, pre-built injection plan later | 🟢 |
| fast path for trivial endpoints | No cases and full kwargs/dependency work always at runtime |
mini compiler to skip deps, validation, kwargs, middlewares if required at startup | 🟢 |
| Route lookup speed | Starlette regex router (slows with many routes) | papaya concurrent hashmap + radix trie lookup |
🟢 |
| Middleware usability (Python) | @app.middleware often buggy / limited |
working decorator + tower-http api |
🟢 |
| Background tasks reliability | Fire 'n forget, errors usually swallowed | proper JoinHandle + error logging |
🟢 |
| WebSocket implementation | Starlette (solid but heavy) | custom with bounded channels + clean async pump | 🟢 |
| Startup-time error detection | Almost everything deferred to runtime | Full signature + dependency analysis at decorator time | 🟢 |
| Deployment footprint | Heavy (uvicorn + many deps) | tiny Rust binary | 🟢 |
| Scaling to 10,000+ routes | Noticeable slowdown | Stays fast thanks to hashmap lookup | 🟢 |
| JSON serialization speed | slow | fast thanks to sonic-rs |
🟢 |
| Prometheus metrics endpoint | No | Yes | 🟢 |
app.mount() / StaticFiles |
Yes | Full support | 🟢 |
Exception Handlers (@app.exception_handler) |
Yes, global error catching | Full support (plus Axum .fallback() alias) |
🟡 |
APIRouter + include_router() |
Yes, mature ecosystem | Full support | 🟡 |
StreamingResponse / SSE |
Yes, chunked streaming | Full support (async & sync generators) | 🟡 |
| Frontend serving support (React, Vue, Svelte, etc.) | Yes | Yes | 🟡 |
Global State (request.app.state) |
Yes | Full support | 🟡 |
response_model=None + raw Response return |
Fully supported | serialization | 🔴 (for now) |
| Concurrency & resource safety | asyncio + threadpool | Native Tokio + Rust memory & thread safety | 🔴 (slow due to context switches) |
Some advanced features are still in development like:
- Add
ORJSONResponse/UJSONResponse - Add
headers,media_type,backgroundparams to response wrapper classes - Add
HTTPSRedirectMiddleware - Actually use
generate_unique_id_functionto generate operation IDs - Support
yield-based dependencies (setup/teardown, e.g.def get_db(): yield db; db.close()) - Support
Annotated[Type, Depends(...)]/Annotated[str, Query(...)]style DI - Execute app-level
dependencies=[...]on every route - Execute router-level
dependencies=[...]frominclude_router/nest/APIRouter(dependencies=...) - Add
app.dependency_overridesfor testing - Dispatch custom
@app.exception_handler(X)handlers instead of only special-casingPyHTTPException - Make
app.statepersistent across requests (not rebuilt per-request scope) - Fix injected
Requestobjects to have workingreceive/sendso.body()/.json()work - Add
url_for() - Expose
request.sessionaccessor for SessionMiddleware - Return structured validation errors (
[{"loc": [...], "msg": ..., "type": ...}]) for path/query/header/cookie params, not just Pydantic body errors - Support repeated query-key list params (
?tags=a&tags=b→List[str]) - Support repeated form-key list params
- Improve scalar coercion for
List[int],Union/Optional, and other complex annotations - Implement
response_model_include - Implement
response_model_exclude - Implement
response_model_by_alias - Implement
response_model_exclude_unset - Implement
response_model_exclude_defaults - Implement
response_model_exclude_none - Add
FileResponse - Add Jinja2Templates equivalent
- Support mounting sub-applications (not just
PyStaticFiles) viaapp.mount() - Support arbitrary Starlette-style ASGI middleware classes
- Support custom
route_class - Logging middlewares
- Async Middleware support
- Full middleware ordering control
- Better error handling (currently shows Rust errors)
- Proper Python-friendly error pages (no Rust tracebacks in production)
- GraphQL support
- Hot reloading / watchfiles integration
- Built-in TestClient (
starlette.testclientstyle) - Advanced dependency scopes (request vs function)
- Rust to Python FFI helpers
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (git checkout -b feature/amazing-feature)
- Commit your changes (git commit -m 'Add some amazing feature')
- Push to the branch (git push origin feature/amazing-feature)
- Open a Pull Request
Check out CONTRIBUTING.md for more details.
This project is licensed under the MIT License - see the LICENSE file for details.

