FastrAPI is a hybrid framework that combines the best of both worlds: Python’s developer-friendly syntax and Rust’s blazing-fast performance. This page explains how the integration works under the hood.
Architecture overview
FastrAPI consists of three main layers:
Python layer
Your application code written in Python with FastrAPI decorators and Pydantic models.
PyO3 bridge
Bidirectional FFI (Foreign Function Interface) that allows Rust and Python to communicate seamlessly.
Rust core
High-performance HTTP server built on Axum, handling routing, middleware, and request processing.
Tokio runtime
Asynchronous task scheduler providing true multi-threaded concurrency.
PyO3: The Python-Rust bridge
PyO3 is a Rust library that enables seamless interoperability between Python and Rust. FastrAPI uses PyO3 version 0.27.0 with these key features:
[ dependencies ]
pyo3 = { version = "0.27.0" , features = [ "extension-module" , "py-clone" ] }
pyo3-async-runtimes = { version = "0.27.0" , features = [ "tokio-runtime" ] }
Module definition
The main FastrAPI module is defined using PyO3’s #[pymodule] macro:
#[pymodule(gil_used = false)]
fn fastrapi ( m : & Bound <' _ , PyModule >) -> PyResult <()> {
m . add_class :: < FastrAPI >() ? ;
m . add ( "FastrAPI" , m . getattr ( "FastrAPI" ) ? ) ? ;
responses :: register ( m ) ? ;
exceptions :: register ( m ) ? ;
request :: register ( m ) ? ;
datastructures :: register ( m ) ? ;
background :: register ( m ) ? ;
security :: register ( m ) ? ;
pydantic :: register_pydantic_integration ( m ) ? ;
status :: create_status_submodule ( m ) ? ;
params :: register ( m ) ? ;
middlewares :: register ( m ) ? ;
websocket :: register ( m ) ? ;
Ok (())
}
The gil_used = false flag indicates that this module doesn’t require holding the Python GIL during initialization, improving startup performance.
Python class binding
The FastrAPI class is exposed to Python using the #[pyclass] macro:
#[pyclass(name = "FastrAPI" )]
pub struct FastrAPI {
#[pyo3(get, set)]
pub debug : bool ,
#[pyo3(get, set)]
pub title : String ,
#[pyo3(get, set)]
pub version : String ,
// ... other fields
}
#[pymethods]
impl FastrAPI {
#[new]
fn new ( py : Python <' _ >, debug : bool , title : String , ... ) -> PyResult < Self > {
// Constructor logic
}
fn get <' py >( & self , path : String , py : Python <' py >) -> PyResult < Py < PyAny >> {
self . create_decorator ( "GET" , path , py )
}
fn serve ( & self , py : Python , host : Option < String >, port : Option < u16 >) -> PyResult <()> {
crate :: server :: serve ( py , host , port , self )
}
}
This allows Python code to interact with Rust objects naturally:
from fastrapi import FastrAPI
app = FastrAPI( debug = True , title = "My API" )
@app.get ( "/hello" )
def hello ():
return { "message" : "world" }
app.serve( "127.0.0.1" , 8080 )
Axum: The HTTP server
Axum is a web framework built on top of Tokio and Hyper. FastrAPI uses Axum 0.8.7 with WebSocket support:
[ dependencies ]
axum = { version = "0.8.7" , features = [ "ws" ] }
tokio = { version = "1.48.0" , features = [ "full" ] }
hyper = "1.7.0"
Router construction
FastrAPI builds an Axum router from Python route definitions:
fn build_router (
py : Python ,
app_state : AppState ,
docs_url : Option < String >,
openapi_url : String ,
app_config : & FastrAPI ,
) -> Router {
let mut app = Router :: new ();
// Register all routes
let guard = local_guard ( &* ROUTES );
for entry in ROUTES . iter ( & guard ) {
let ( route_key , _handler ) = entry ;
let parts : Vec < & str > = route_key . splitn ( 2 , ' ' ) . collect ();
let method = parts [ 0 ];
let path = parts [ 1 ] . to_string ();
app = register_route ( app , method , path , route_key . as_str () . into (), app_state . clone ());
}
// Apply middleware layers
app . layer ( Extension ( app_state ))
}
Request handling flow
GET/HEAD/OPTIONS
POST/PUT/DELETE/PATCH
For routes without request bodies: let handler = move | Extension ( state ) : Extension < AppState >,
ConnectInfo ( _addr ) : ConnectInfo < SocketAddr > | {
let route_key = Arc :: clone ( & route_key_clone );
async move {
run_py_handler_no_args ( state . rt_handle, route_key ) . await
}
};
app . route ( & path , get ( handler ))
For routes with request bodies: let handler = move | Extension ( state ) : Extension < AppState >,
ConnectInfo ( _addr ) : ConnectInfo < SocketAddr >,
Json ( payload ) | {
let route_key = Arc :: clone ( & route_key_clone );
async move {
run_py_handler_with_args ( state . rt_handle, route_key , payload ) . await
}
};
app . route ( & path , post ( handler ))
Data structure bridging
Route storage
FastrAPI uses lock-free concurrent data structures from the papaya crate:
use papaya :: HashMap as PapayaHashMap ;
use once_cell :: sync :: Lazy ;
pub static ROUTES : Lazy < PapayaHashMap < String , RouteHandler >> =
Lazy :: new ( || PapayaHashMap :: with_capacity ( 128 ));
pub struct RouteHandler {
pub func : Py < PyAny >, // Python function reference
pub is_async : bool , // Async detection
pub is_fast_path : bool , // Fast-path optimization flag
pub param_validators : Vec <( String , Py < PyAny >)>,
pub response_type : ResponseType ,
pub needs_kwargs : bool ,
pub path_param_names : Vec < String >,
pub query_param_names : Vec < String >,
pub body_param_names : Vec < String >,
pub dependencies : Vec < DependencyInfo >,
}
Python to Rust conversion
FastrAPI uses serde-pyobject to convert between Python objects and Rust types:
use serde_json :: Value ;
use pythonize :: depythonize;
fn py_any_to_json ( py : Python , obj : & Bound < PyAny >) -> Value {
match depythonize :: < Value >( obj ) {
Ok ( val ) => val ,
Err ( _ ) => Value :: Null ,
}
}
Async execution model
Dual runtime architecture
FastrAPI maintains two separate runtimes:
Tokio runtime Handles all async I/O, routing, and middleware: // Main event loop
PYTHON_RUNTIME . block_on ( async move {
let listener = TcpListener :: bind ( & addr ) . await ? ;
axum :: serve ( listener , router ) . await
});
Python thread pool Executes Python handlers in isolation: // Spawn blocking Python work
rt_handle . spawn_blocking ( move || {
Python :: attach ( | py | {
// Execute Python code with GIL
handler . func . call0 ( py )
})
}) . await
Async Python functions
For Python async def functions, FastrAPI bridges to Tokio using pyo3-async-runtimes:
let future = Python :: attach ( | py | -> PyResult < _ > {
let bound_func = func . bind ( py );
let bound_kwargs = py_kwargs . bind ( py );
let coro = bound_func . call ((), Some ( bound_kwargs )) ? ;
// Convert Python coroutine to Rust future
pyo3_async_runtimes :: tokio :: into_future ( coro )
}) ? ;
let result = future . await ? ; // Await in Tokio runtime
Dependency injection
FastrAPI implements dependency injection entirely in Rust:
Dependency parsing
pub struct DependencyInfo {
pub func : Py < PyAny >,
pub is_async : bool ,
pub param_name : Option < String >,
pub scopes : Vec < String >,
pub use_cache : bool ,
pub sub_dependencies : Vec < DependencyInfo >,
pub injection_plan : Vec <( String , InjectionType )>,
}
pub enum InjectionType {
Dependency ( String ), // Another dependency
Parameter , // Path/query param
Request , // Request object
SecurityScopes , // Security scopes
}
Pre-computed injection plans
At decorator time, FastrAPI analyzes the dependency graph:
pub fn parse_dependencies ( py : Python , func : & Bound < PyAny >) -> PyResult < Vec < DependencyInfo >> {
let mut dependencies = Vec :: new ();
let inspect = py . import ( "inspect" ) ? ;
let signature = inspect . call_method1 ( "signature" , ( func ,)) ? ;
// Build injection plan once
let injection_plan = build_injection_plan ( py , func , & sub_deps , & scopes ) ? ;
dependencies . push ( DependencyInfo {
func : target_callable ,
is_async ,
injection_plan , // Cached for all future requests
// ...
});
Ok ( dependencies )
}
This eliminates runtime introspection overhead on every request.
Key dependencies
FastrAPI relies on these Rust crates:
Crate Version Purpose pyo3 0.27.0 Python-Rust FFI bridge axum 0.8.7 HTTP server framework tokio 1.48.0 Async runtime papaya 0.2.3 Lock-free concurrent hashmap serde_json 1.0.145 JSON serialization tower-http 0.6.6 Middleware (CORS, GZip, compression) tower-sessions 0.14.0 Session management dashmap 6.1.0 Concurrent hashmap hyper 1.7.0 Low-level HTTP primitives
Compile-time optimizations
FastrAPI’s release profile is tuned for maximum performance:
[ profile . release ]
codegen-units = 1 # Better inlining across crate boundaries
lto = "fat" # Full link-time optimization
panic = "abort" # No unwinding, smaller binary
strip = true # Remove debug symbols
opt-level = 3 # Maximum optimization level
[ profile . release . build-override ]
opt-level = 3 # Optimize build scripts too
Zero-copy string handling
FastrAPI uses smartstring for small string optimizations:
use smartstring :: alias :: String as SmartString ;
// Stores strings up to 23 bytes inline (no heap allocation)
let route_key : SmartString = format! ( "GET /api/users/{}" , id ) . into ();
Parking lot for faster locks
Where locks are necessary, FastrAPI uses parking_lot for lower overhead:
use parking_lot :: RwLock ; // Faster than std::sync::RwLock
let data = Arc :: new ( RwLock :: new ( shared_state ));
Building from source
To build FastrAPI locally:
Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Clone the repository
git clone https://github.com/ppmpreetham/fastrapi.git
cd fastrapi
Build with maturin
pip install maturin
maturin develop --release
Test the build
from fastrapi import FastrAPI
app = FastrAPI()
@app.get ( "/test" )
def test ():
return { "status" : "built from source!" }
app.serve( "127.0.0.1" , 8080 )
Extending FastrAPI
You can add custom Rust handlers for maximum performance:
Native Rust endpoints
// In src/custom_handlers.rs
use axum :: { Json , response :: IntoResponse };
use serde_json :: json;
pub async fn ultra_fast_endpoint () -> impl IntoResponse {
// Pure Rust, no Python overhead
Json ( json! ({ "message" : "blazingly fast" }))
}
Register in module
#[pymodule(gil_used = false)]
fn fastrapi ( m : & Bound <' _ , PyModule >) -> PyResult <()> {
m . add_class :: < FastrAPI >() ? ;
// Expose Rust function to Python
m . add_function ( wrap_pyfunction! ( ultra_fast_endpoint , m ) ? ) ? ;
Ok (())
}
Custom Rust handlers require rebuilding the extension module and won’t benefit from Python’s dynamic nature.
Debugging tips
FastrAPI uses the tracing crate for structured logging: app = FastrAPI( debug = True )
Set the RUST_LOG environment variable: RUST_LOG = debug python main.py
Python traceback in Rust panics
If a Rust panic occurs, enable backtraces: RUST_BACKTRACE = 1 python main.py
Use py-spy to profile Python code called from Rust: pip install py-spy
py-spy record -o profile.svg -- python main.py
Further reading
PyO3 User Guide Learn more about Python-Rust interoperability
Axum Documentation Deep dive into Axum’s routing and middleware
Tokio Tutorial Master async Rust with Tokio
FastrAPI Source Explore the full implementation