Closed nickchomey closed 2 months ago
Hi @nickchomey!
Caddy snake doesn't have ASGI support yet. But this project might be useful to run ASGI apps with a WSGI server: https://github.com/abersheeran/a2wsgi.
I tested with a simple FastAPI app and seems to work:
example_fastapi.py
from fastapi import FastAPI
from a2wsgi import ASGIMiddleware
app = FastAPI()
@app.get("/app")
async def root():
return {"message": "Hello World"}
app = ASGIMiddleware(app)
Caddyfile
{
http_port 9080
https_port 9443
log {
level info
}
}
localhost:9080 {
route /app {
python "example_fastapi:app"
}
}
I think this is just a workaround for now.
Thanks for your comments and for filing the issue. And hope this helps. Im gonna start working on ASGI support as soon as possible
Now that I think about it some more (it's been a little while since I've worked with Python, FastAPI etc...), perhaps this is a non-issue...
FastAPI is, among other things, meant for running a long-running async server. You start it once and it receives requests, processes them and returns the result.
As such, surely we can just use Caddy as a reverse proxy without any WSGI/ASGI stuff...
Even still, FastAPI itself has documentation about how to use Gunicorn (WSGI) in front of Uvicorn (ASGI). https://fastapi.tiangolo.com/deployment/server-workers/
Perhaps that could be used here somehow?
One side/clarifying question: it looks like what this plugin does is allow you to start a new python process upon each request that hits Caddy. If so, under what scenarios would that be desirable as opposed to just reverse proxying to a long-running server (like FastAPI)? Surely there's extra overhead to bootstrapping it all for each request
Or, again, perhaps I'm misunderstanding/forgetting something fundamental here...
Thanks!
This plugin is meant to avoid adding an extra layer of reverse proxy. What the plugin is doing is embedding the Python interpreter inside Caddy. It doesn't create a new process on each request. The requests are all handled within Caddy.
To make it clear, when you configure a Caddyfile like this:
route /app {
python "example_fastapi:app"
}
What happens internally is that Caddy imports the FastAPI app using the Python C API and is initialized on startup, the FastAPI server runs inside Caddy from then on.
On each request to /app
, Caddy passes the data directly to FastAPI "in-memory" without creating a new process.
Ah, very cool! Clearly I was, indeed, missing something fundamental!
And, now that I think about it more, what I was describing in my previous comment was wrong. I think Uvicorn and/or gunicorn are the long-running servers, hosting the fastapi app.
So,this plugin replaces uvicorn/gunicorn rather than fastapi. Sort of in the same sense that the popular new Frankenphp caddy module replaces php-fpm and calls the php application code directly.
Is that correct?
(sorry again, it's been a little while since I've worked with Python and just stumbled upon this as I start to get into Caddy)
Yep, exactly like that.
It serves the same purpose as gunicorn/uvicorn.
And indeed this plugin works similarly as Frankenphp replacing the php-fpm calls.
Fantastic! Thanks for your patience and explanations. I look forward to using this when I get back to incorporating Python into my application architecture! Embedding languages right into Caddy seems to be the future!
Thank you!
I'm here to help, don't hesitate in asking other questions if further clarification is needed.
ASGI HTTP support is out!
There's a new directive to use it: module_asgi
.
You can import your apps as follows:
route /app {
python {
module_asgi "example_fastapi:app"
}
}
This doesn't require any external dependencies and should work out of the box.
Noting here that it only supports HTTP for now, Websockets is not supported yet.
This is fantastic! But I'm wondering if there's any sort of ASGI support, in order to run FastAPI apps?
Or am I misunderstanding how this all works and there's another approach for this?