FastAPI-compatible web framework with Zig HTTP core — 20x faster with Python 3.14 free-threading
Project description
TurboAPI
FastAPI-compatible Python framework. Zig HTTP core. 20x faster.
Drop-in replacement · Zig-native validation · Zero-copy responses · Free-threading · dhi models
Status · Quick Start · Benchmarks · Architecture · Migrate
🏷 Status
Alpha — TurboAPI works and is tested (253+ passing tests), but the API surface may change and some features are still in progress.
| What works today | What's in progress |
|---|---|
| ✅ FastAPI-compatible route decorators | 🔧 WebSocket support |
| ✅ Zig HTTP server with 24-thread pool + keep-alive | 🔧 HTTP/2 and TLS |
| ✅ Zig-native JSON schema validation (dhi) | 🔧 Buffer pool reuse across requests |
| ✅ Zero-copy response pipeline | 🔧 Cloudflare Workers WASM target |
✅ Zig-side JSON→Python dict (no json.loads) |
|
| ✅ Async handler support | |
| ✅ Full security stack (OAuth2, Bearer, API Key) | |
| ✅ Python 3.14t free-threaded support | |
| ✅ Native FFI handlers (C/Zig, no Python at all) |
Use TurboAPI if you want the fastest possible Python API framework and are comfortable with an alpha project. Don't use it for production workloads without thorough testing first.
⚡ Quick Start
Requirements: Python 3.13+ free-threaded (3.14t recommended)
pip install turboapi
from turboapi import TurboAPI
from dhi import BaseModel
app = TurboAPI()
class Item(BaseModel):
name: str
price: float
quantity: int = 1
@app.get("/")
def hello():
return {"message": "Hello World"}
@app.get("/items/{item_id}")
def get_item(item_id: int):
return {"item_id": item_id, "name": "Widget"}
@app.post("/items")
def create_item(item: Item):
return {"item": item.model_dump(), "created": True}
app.run()
That's it. Your API is running on a Zig HTTP server at http://localhost:8000.
📊 Benchmarks
All numbers verified with correct, identical JSON responses. wrk -t4 -c100 -d8s, Python 3.14t free-threaded, Apple Silicon M3 Pro.
Throughput (req/s)
─────────────────────────────────────────────────────────────
GET /ping ██████████████████████████████████████████████ 139,350 ← TurboAPI
(simple_sync_noargs) ███ 6,847 ← FastAPI
GET /items/{id} █████████████████████████████████ ~105,000
███ 8,666
POST /items (JSON) ████████████████████████████████ ~95,000
████ 8,200
─────────────────────────────────────────────────────────────
Average speedup: ~20x Avg latency: 0.16ms vs 14.6ms
Zero-arg GET handlers use PyObject_CallNoArgs — no empty tuple/kwargs allocation. POST validation runs in Zig (dhi schema) before acquiring the GIL. Invalid bodies return 422 without calling Python.
Run your own benchmarks
Run your own benchmarks
python benchmarks/turboapi_vs_fastapi.py
python benchmarks/turboapi_vs_fastapi.py --duration 10 --threads 4 --connections 100
⚙️ Architecture
Request lifecycle
Every HTTP request flows through the same pipeline. The key idea: Python only runs your business logic. Everything else — parsing, routing, validation, response writing — happens in Zig.
┌──────────────────────────────────────────────────────┐
│ Zig HTTP Core │
HTTP Request ──────►│ │
│ TCP accept ──► header parse ──► route match │
│ (24-thread pool) (8KB buf) (radix trie) │
│ │
│ Content-Length body read (dynamic alloc, 16MB cap) │
└────────────────────┬─────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ Native FFI │ │ model_sync │ │ simple_sync │
│ (no Python) │ │ │ │ body_sync │
│ │ │ JSON parse in Zig │ │ │
│ C handler ───┤ │ dhi schema validate │ │ Acquire GIL │
│ direct call │ │ ▼ fail → 422 │ │ call handler│
│ (no GIL) │ │ ▼ pass → Zig builds │ │ zero-copy │
│ │ │ Python dict from │ │ write │
└──────┬────────┘ │ parsed JSON │ └──────┬───────┘
│ │ model(**data) │ │
│ │ handler(model) │ │
│ │ zero-copy write │ │
│ └──────────┬────────────┘ │
│ │ │
└────────────────────────┴──────────────────────┘
│
┌────▼─────┐
│ Response │
│ (keep- │
│ alive) │
└──────────┘
What "zero-copy" means
On the response path, Zig calls PyUnicode_AsUTF8() to get a pointer to the Python string's internal buffer, then calls write() directly on the socket. No memcpy, no temporary buffers, no heap allocation. The Python string stays alive because we hold a reference to it.
Handler classification
At startup, each route is analyzed once and assigned the lightest dispatch path:
| Handler type | What it skips | When used |
|---|---|---|
native_ffi |
Python entirely — no GIL, no interpreter | C/Zig shared library handlers |
simple_sync_noargs |
GIL lookup, tuple/kwargs alloc — uses PyObject_CallNoArgs |
Zero-param GET handlers |
model_sync |
json.loads — Zig parses JSON and builds Python dict |
POST with a dhi.BaseModel param |
simple_sync |
header parsing, body parsing, regex | GET handlers with path/query params |
body_sync |
header parsing, regex | POST without model params |
enhanced |
nothing — full Python dispatch | Depends(), middleware, complex types |
Zig-side JSON parsing (model_sync)
For model_sync routes, the JSON request body is parsed twice in Zig, zero times in Python:
- dhi validation —
dhi_validator.zigparses the JSON and validates field types, constraints (min_length,gt, etc.), nested objects, and unions. Invalid requests get a422without acquiring the GIL. - Python dict construction —
jsonValueToPyObject()inserver.zigrecursively converts the parsedstd.json.Valuetree into Python objects (PyDict,PyList,PyUnicode,PyLong,PyFloat,PyBool,Py_None). The resulting dict is passed to the handler asbody_dict.
The Python handler receives a pre-built dict and just does model_class(**data) — no json.loads, no parsing overhead.
🚀 Features
Drop-in FastAPI replacement
# Before
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
# After
from turboapi import TurboAPI as FastAPI, Depends, HTTPException
from dhi import BaseModel
Everything else stays the same. Routes, decorators, dependency injection, middleware — all compatible.
Zig-native validation via dhi
from dhi import BaseModel, Field
class CreateUser(BaseModel):
name: str = Field(min_length=1, max_length=100)
email: str
age: int = Field(gt=0, le=150)
@app.post("/users")
def create_user(user: CreateUser):
return {"created": True, "user": user.model_dump()}
Model schemas are extracted at startup and compiled into Zig. Invalid requests get rejected with a 422 before touching Python — no GIL acquired, no handler called. Valid requests are passed to your handler with a real model instance.
Async handlers
@app.get("/async")
async def async_handler():
data = await fetch_from_database()
return {"data": data}
Async handlers are automatically detected and awaited via asyncio.run().
Full security stack
from turboapi import Depends, HTTPException
from turboapi.security import OAuth2PasswordBearer, HTTPBearer, APIKeyHeader
oauth2 = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/protected")
def protected(token: str = Depends(oauth2)):
if token != "secret":
raise HTTPException(status_code=401, detail="Invalid token")
return {"user": "authenticated"}
OAuth2, HTTP Bearer/Basic, API Key (header/query/cookie) — all supported with correct status codes (401/403).
Native FFI handlers
Skip Python entirely for maximum throughput:
# Register a handler from a compiled shared library
app.add_native_route("GET", "/fast", "./libhandler.so", "handle_request")
The Zig server calls the C function directly — no GIL, no interpreter, no overhead.
🔄 Migrating from FastAPI
Step 1: Swap the imports
# Before
from fastapi import FastAPI, Depends, HTTPException, Query, Path
from pydantic import BaseModel
# After
from turboapi import TurboAPI as FastAPI, Depends, HTTPException, Query, Path
from dhi import BaseModel
Step 2: Use the built-in server
# FastAPI way (still works)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
# TurboAPI way (20x faster)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
Step 3: Run with free-threading
# Install free-threaded Python
uv python install 3.14t
python3.14t app.py
Feature Parity
| Feature | Status |
|---|---|
| Route decorators (@get, @post, etc.) | ✅ |
| Path parameters with type coercion | ✅ |
| Query parameters | ✅ |
| JSON request body | ✅ |
| Async handlers | ✅ |
Dependency injection (Depends()) |
✅ |
| OAuth2 (Password, AuthCode) | ✅ |
| HTTP Bearer / Basic auth | ✅ |
| API Key (Header / Query / Cookie) | ✅ |
| CORS middleware | ✅ |
| GZip middleware | ✅ |
| HTTPException with status codes | ✅ |
| Custom responses (JSON, HTML, Redirect) | ✅ |
| Background tasks | ✅ |
| APIRouter with prefixes | ✅ |
| Native FFI handlers (C/Zig, no Python) | ✅ |
| Zig-native JSON schema validation (dhi) | ✅ |
| Zig-side JSON→Python dict (no json.loads) | ✅ |
| Large body support (up to 16MB) | ✅ |
| Python 3.14t free-threaded | ✅ |
| WebSocket support | 🔧 In progress |
| HTTP/2 + TLS | 🔧 In progress |
📁 Project Structure
turboAPI/
├── python/turboapi/
│ ├── main_app.py # TurboAPI class (FastAPI-compatible)
│ ├── zig_integration.py # route registration, handler classification
│ ├── request_handler.py # enhanced/fast/fast_model handlers
│ ├── security.py # OAuth2, HTTPBearer, APIKey, Depends
│ ├── version_check.py # free-threading detection
│ └── turbonet.*.so # compiled Zig extension
├── zig/src/
│ ├── main.zig # Python C extension entry
│ ├── server.zig # HTTP server, thread pool, dispatch, JSON→PyObject
│ ├── router.zig # radix trie with path params + wildcards
│ ├── dhi_validator.zig # runtime JSON schema validation
│ └── py.zig # Python C-API wrappers
├── tests/ # 253+ tests
├── benchmarks/
│ └── turboapi_vs_fastapi.py
└── zig/build.zig
Building from Source
git clone https://github.com/justrach/turboAPI.git
cd turboAPI
# Build the Zig extension for your Python
python3.14t zig/build_turbonet.py --install
# Install the Python package
pip install -e .
# Run tests
python -m pytest tests/ -v
🤝 Contributing
Open an issue before submitting a large PR so we can align on the approach.
git clone https://github.com/justrach/turboAPI.git
cd turboAPI
python -m pytest tests/ # make sure tests pass before and after your change
Credits
- dhi — Pydantic-compatible validation, Zig + Python
- Zig 0.15 — HTTP server, JSON validation, zero-copy I/O
- Python 3.14t — free-threaded runtime, true parallelism
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file turboapi-1.0.0rc6.tar.gz.
File metadata
- Download URL: turboapi-1.0.0rc6.tar.gz
- Upload date:
- Size: 107.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec1bbb546c85785516670c1b45c15ae636e93efa2127e35ae7a7e28889636839
|
|
| MD5 |
e702b9ce4f751d5a0b82fb90d2c760cb
|
|
| BLAKE2b-256 |
0f1f202a94700b83efaa961ff5f80c2ede3e7886d83ac178c8bdd00c517fb4d6
|