[Python] Where to store the database client in FastAPI?

Discussão em 'Python' iniciado por Stack, Dezembro 9, 2024.

  1. Stack

    Stack Membro Participativo

    I have a FastAPI app with MongoDB connected via Motor. I open a MongoDB connection at the beginning of an app and close it at the end using lifespan. Reading documentation and other people's Github repositories I have found 3 main ways to store the client.

    1. Dependency Injection with global variables


    from contextlib import asynccontextmanager
    from fastapi import FastAPI, Depends
    from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase

    mongo_client = None

    def init_db():
    global mongo_client
    mongo_client = AsyncIOMotorClient("mongo://")

    def get_db():
    return mongo_client["mydb"]

    def close_db():
    mongo_client.close()



    @asynccontextmanager
    async def lifespan(app):
    init_db()
    yield
    close_db()


    app = FastAPI(lifespan=lifespan)


    @app.get("/")
    async def root(db: Annotated[AsyncIOMotorDatabase, Depends(get_db)]):
    return db


    But global variables are highly discouraged in Python.

    2. Dependency Injection with a Singleton Pattern


    Basically, the same as global variables. All pros and cons of global variables. Also an anti-pattern as global variables.

    3. Storing database in lifespan state


    There is another way to store database client, which is in the lifespan state, and then it will be tagged along every request (if I understand correctly), and can be read from there:

    from contextlib import asynccontextmanager
    from fastapi import FastAPI, Request
    from motor.motor_asyncio import AsyncIOMotorClient


    @asynccontextmanager
    async def lifespan(app):
    mongo_client = AsyncIOMotorClient("mongo://")
    mongo_db = mongo_client["mydb"]
    yield {"db": mongo_db}
    mongo_client.close()


    app = FastAPI(lifespan=lifespan)


    @app.get("/")
    async def root(request: Request):
    return request.state.db


    But then I cannot help but feel weird to pass database client to Request state and hold it with every request. It seems inappropriate to store a sensitive information about database client in requests and tag it along with every request all the time.

    Dependency injection seems more appropriate for this use case but it practically requires global variables (unless I use some ORM which would store a reference to the database for me).

    What is the best practice here?

    Continue reading...

Compartilhe esta Página