Skip to main content

FastAPI-compatible web framework with Zig HTTP core - 2-3x faster with Python 3.14 free-threading

Project description

TurboAPI

PyPI version License Python 3.13+ Zig 0.15 Alpha Ask DeepWiki

TurboAPI

FastAPI-compatible Python framework. Zig HTTP core. 7x 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 (230+ 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 8-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 -d10s, Python 3.14t free-threaded, Apple Silicon.

  Throughput (req/s)
  ─────────────────────────────────────────────────────────────

  GET /               ████████████████████████████████████  71,290  ← TurboAPI
                      █████  10,038                                 ← FastAPI

  GET /items/{id}     █████████████████████████████████  65,266
                      ████  8,666

  GET /users/../posts █████████████████████████████████  60,911
                      ████  8,357

  POST /items (JSON)  ██████████████████████████████  59,595
                      ████  8,200

  ─────────────────────────────────────────────────────────────
  Average speedup: 7.3x    Average latency: ~120μs vs ~12ms

POST validation runs in Zig (dhi schema) before acquiring the GIL. Invalid bodies return 422 directly — the Python handler is never called.

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          │
                      │       (8-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
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 no body
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:

  1. dhi validationdhi_validator.zig parses the JSON and validates field types, constraints (min_length, gt, etc.), nested objects, and unions. Invalid requests get a 422 without acquiring the GIL.
  2. Python dict constructionjsonValueToPyObject() in server.zig recursively converts the parsed std.json.Value tree into Python objects (PyDict, PyList, PyUnicode, PyLong, PyFloat, PyBool, Py_None). The resulting dict is passed to the handler as body_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 (7x 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/                    # 230+ 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

turboapi-1.0.0a1.tar.gz (102.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

turboapi-1.0.0a1-py3-none-any.whl (65.5 kB view details)

Uploaded Python 3

File details

Details for the file turboapi-1.0.0a1.tar.gz.

File metadata

  • Download URL: turboapi-1.0.0a1.tar.gz
  • Upload date:
  • Size: 102.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for turboapi-1.0.0a1.tar.gz
Algorithm Hash digest
SHA256 7fd52789a2179f62304c0eccb96eac8c35fe59ca82b382a92ff6fea2bacb31de
MD5 83abb66b3b862cc0bb8fac9482ee7653
BLAKE2b-256 bd7eedea7e025198debb68bc4959ae60076d15b6709568cc63a6371f24a729bb

See more details on using hashes here.

File details

Details for the file turboapi-1.0.0a1-py3-none-any.whl.

File metadata

  • Download URL: turboapi-1.0.0a1-py3-none-any.whl
  • Upload date:
  • Size: 65.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for turboapi-1.0.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 85fbab8d194bfd1376cfe58d994343346c651c60d1695e32dba8a7ebe4ca7a8b
MD5 8a7d33c8d1ec42d04489ee86bf783c5e
BLAKE2b-256 eb8b4b7e8f293508c84d3e6c8f5ee5b3ef522a29f28e280ef80e1785e4ab5084

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page