ASGI (FastAPI / Starlette / Quart / …)¶
TusASGIApp is a generic ASGI adapter that wraps a synchronous TusServer and runs handle_request on a thread pool via asyncio.to_thread. The event loop stays free, no async storage rewrite required.
Mount on FastAPI¶
from fastapi import FastAPI
from resumable_upload import SQLiteStorage, TusServer
from resumable_upload.asgi import TusASGIApp
app = FastAPI()
tus = TusServer(storage=SQLiteStorage(), base_path="/files")
app.mount("/files", TusASGIApp(tus))
That's the entire integration. Mount on any path you like — TusASGIApp derives the request path from the ASGI scope, so the mount point doesn't have to match base_path.
Mount on Starlette¶
from starlette.applications import Starlette
from starlette.routing import Mount
from resumable_upload import SQLiteStorage, TusServer
from resumable_upload.asgi import TusASGIApp
tus = TusServer(storage=SQLiteStorage(), base_path="/files")
app = Starlette(routes=[Mount("/files", app=TusASGIApp(tus))])
What the adapter does¶
- Drains the request body off the ASGI receive channel into a single
bytesbuffer (the sync handler does not stream). - Decodes scope headers from latin-1 byte tuples into a string dict.
- Hands
(method, path, headers, body)toTusServer.handle_requeston a worker thread. - Streams the response back as one
http.response.start+ onehttp.response.bodymessage.
lifespan events are accepted and acknowledged but otherwise ignored — there's no startup or shutdown work to perform. Non-HTTP scopes raise NotImplementedError.
Examples¶
examples/server/asgi_app.py— minimal Starlette mount.examples/server/fastapi_app.py— full FastAPI integration.