I've been using flask and tornado for some time, and I used to create a DB engine and Redis client and something like the fileserver, then bind them to app.config in the main app.py file.
# in the main app,py file
from sqlalchemy import create_engine
from redis import Redis
dbe = create_engine(...)
app.config['dbe'] = dbe
redis_cli = Redis(...)
app.config['redis_cli'] = redis_cli
then I can from flask import current_app as app and use the app.config['dbe'] to execute sqls.
# in the app view file
from flask import current_app as app
app.config['dbe'].execute(sql, **kwargs)
app.config['redis_cli'].get(...)
With this method, I don't need to initialize the DB engine and Redis client every time, and all the pre-initialized DB engine can be used in the app views(i just treat them as handlers or routers)
I'm wondering is there an equivalent method in fastAPI? Or, I need to create them every time?
thanks for the reply, it seems that tiangolo thinks And thread locals are a tool that only works with simple threaded applications, it doesn't work in async contexts. , and he mentioned In those cases (and all the cases in modern Python) you need instead context vars., but how could I create context vars in fastapi?
It is possible to do this and very useful.
Although currently it means making a change to Starlette. Which (according to my needs) incorrectly overwrites the 'app' parameter set in the ASGI context. See https://github.com/encode/starlette/issues/977
In my code (which has only been tested in development! ) I'm using app.extra['redis'] to store a redis connection pool.
I have something along the lines of ...
@app.on_event("startup")
async def handle_startup():
try:
pool = await aioredis.create_redis_pool(
(REDIS_HOST, REDIS_PORT), encoding='utf-8')
logger.info(f"Connected to Redis on {REDIS_HOST} {REDIS_PORT}")
app.extra['redis'] = pool
except ConnectionRefusedError as e:
logger.info(f"cannot connect to redis on {REDIS_HOST} {REDIS_PORT}")
return
Then in my endpoint handlers i'll have something like...
@router.put("/blah/de/blah/")
async def update(request: Request)
redis = request.app.extra['redis']
It has worked for me so far in my limited testing.... I'd be interested if others can see any problems with this approach?
I tried Contextvars but didnt work for me seems to only make sense to for long lived coroutines for example a websocket connection.
Works the same way for websockets, like:
@router.websocket_route("/ws")
class WSEndpoint(WebSocketEndpoint):
async def on_connect(self, websocket: WebSocket) -> None:
await websocket.app.extra['redis'].info()
I just find that nsidnev made a solution in https://github.com/nsidnev/fastapi-realworld-example-app and the way he/she did it is storing DB connection in app.state.pool, which is actually the same solution in #1028 provided by mikelibg as well as what a137x did above, also leonh used the same solution. But storing DB connection this way might not be recommended, since we didn't get a confirmation in #1028 from tigalo, there might be some profound reasons. Hope someone could explain this in a more detailed way. Thanks in advance.
Most helpful comment
It is possible to do this and very useful.
Although currently it means making a change to Starlette. Which (according to my needs) incorrectly overwrites the 'app' parameter set in the ASGI context. See https://github.com/encode/starlette/issues/977
In my code (which has only been tested in development! ) I'm using app.extra['redis'] to store a redis connection pool.
I have something along the lines of ...
Then in my endpoint handlers i'll have something like...
It has worked for me so far in my limited testing.... I'd be interested if others can see any problems with this approach?
I tried Contextvars but didnt work for me seems to only make sense to for long lived coroutines for example a websocket connection.