FastAPI Integration
Serve any FastAPI application over HTTP/3 with a single line change. nhttp3 is a drop-in replacement for uvicorn that speaks QUIC.
Migration from uvicorn
Before (HTTP/1.1 + HTTP/2)
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
After (HTTP/3)
import nhttp3
nhttp3.serve(app, host="0.0.0.0", port=4433,
certfile="cert.pem", keyfile="key.pem")
Why port 4433?
HTTP/3 uses UDP, not TCP. The standard HTTPS port 443 is typically used by TCP-based servers. 4433 is a common convention for HTTP/3 development. In production, you can use port 443 for both TCP (HTTP/2) and UDP (HTTP/3) simultaneously.
Full Example
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, StreamingResponse
import nhttp3, asyncio
app = FastAPI(title="My API over HTTP/3")
@app.get("/")
async def root():
return {"message": "Hello from HTTP/3!"}
@app.post("/echo")
async def echo(request: Request):
body = await request.body()
return {"echo": body.decode(), "protocol": "h3"}
@app.get("/stream")
async def stream():
"""HTTP/3 advantage: each SSE chunk is on its own stream."""
async def generate():
for i in range(10):
yield f"data: chunk {i}\n\n"
await asyncio.sleep(0.1)
return StreamingResponse(generate(), media_type="text/event-stream")
nhttp3.serve(app, port=4433, certfile="cert.pem", keyfile="key.pem")
Testing Your Server
# GET
curl --http3 https://localhost:4433/ -k
# POST with body
curl --http3 https://localhost:4433/echo -X POST -d "hello" -k
# Streaming response
curl --http3 https://localhost:4433/stream -k --no-buffer
# With timing info
curl --http3 https://localhost:4433/ -k \
-w "\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n"
Why HTTP/3 for APIs?
| Feature | HTTP/2 (uvicorn) | HTTP/3 (nhttp3) |
|---|---|---|
| Connection setup | TCP + TLS = 2 RTT | QUIC + TLS = 1 RTT |
| Head-of-line blocking | Yes (shared TCP) | None (per-stream) |
| Network change | Connection drops | Connection migrates |
| Header compression | HPACK | QPACK (50% savings) |
| Multiplexing | Over one TCP conn | Independent QUIC streams |
When HTTP/3 matters most
- High-latency clients — Mobile, edge, cross-continental. 1-RTT saves real milliseconds.
- Streaming responses — SSE, WebSocket-like patterns. No HOL blocking.
- Parallel API calls — Frontend making 10+ concurrent requests. Each independent.
- Unreliable networks — Single packet loss doesn't stall all requests.
Configuration Options
nhttp3.serve(
app,
host="0.0.0.0", # Bind address
port=4433, # UDP port
certfile="cert.pem", # TLS certificate
keyfile="key.pem", # TLS private key
)
Advanced configuration via nhttp3.Config():
config = nhttp3.Config()
config.max_idle_timeout = 60.0 # seconds
config.initial_max_streams_bidi = 200 # concurrent request streams
config.initial_max_data = 50_000_000 # 50MB connection flow control
config.enable_0rtt = True # 0-RTT resumption
Source Code
examples/fastapi/server.py — Complete example with streaming, echo, and large responses.
Agent-friendly copy
Markdown: docs/guides/python-server.md