#Accessing SQLAlchemy Database From Auth Middleware

1 messages · Page 1 of 1 (latest)

crisp dock
#

I have implemented an auth middleware based on BaseDeviceAuthMiddleware and need to access the database from within the middleware. I found that accessing dependencies like you normally would with route handlers is not possible via middleware. I then found by searching posts here that I should be using SQLAlchemyAsyncConfig.provide_transaction() to get access to a session from middleware.

What's the safest/cleanest way to get get access to provide_transaction() from within my middleware class?

Right now, in my create_app(), after I create the app, I store a reference to the provide_transaction function in the Litestar app state:

__init__.py:

def create_app() -> Litestar:
    engine_config = EngineConfig(echo=True)

    db_config = SQLAlchemyAsyncConfig(
        connection_string=build_database_uri(),
        metadata=Base.metadata,
        create_all=True,
        engine_config=engine_config,
    )

    from .devices_controller import DevicesController

    app = Litestar(
        route_handlers=[DevicesController],
        dependencies={'transaction': provide_transaction},
        plugins=[SQLAlchemyPlugin(db_config)]
    )

    app.state.provide_session = db_config.provide_session

    return app

Then in my middleware, I reference it like so:

auth_middleware.py:

    async def authenticate_request(self, connection: ASGIConnection) -> AuthenticationResult:
        ...
        async with connection.app.state.provide_session(connection.app.state, connection.scope) as session:
            result = await session.execute(...)
        ...

This seems to work fine, but feels clunky. Is there a better way than storing a reference to provide_session in the app state? Is this a safe method? I also considered having my db_config instance stored in a separate Python module that I could import from both create_app and in the middleware.

dawn plumeBOT
#
Notes for Accessing SQLAlchemy Database From Auth Middleware
At your assistance

@crisp dock

No Response?

If no response in a reasonable time, ping @Member.

Closing

To close, type !solve or byte solve.

MCVE

Please include an MCVE so that we can reproduce your issue locally.

sharp escarp
#

You can choose provide_session or get_session to get session

crisp dock
#

Thanks! Yes, I was looking at that (that's part of how I got on the right track) but all the code in that example is in one file and it's not using an app factory pattern. It becomes a little more complicated to access the config when it's created in another file and I create the config next to the app in the app factory. Like I mentioned, I could factor the config creation out into its own Python file/module and just import that in both places. Is that a better approach than the current approach I am doing of storing a reference to the function in app.state? What I have works, just wondering if it's proper/safe or if there's a better way when I have my code in separate files and using an app factory.

sharp escarp
#

provide_session provides a session instance from request state if it exists, or creates a new session if it doesn’t, while get_session always returns a new instance from the session maker.

provide_session takes state and scope so you can use provide_session wherever you need if you can get access to state and scope, while get_session does not need them.

#

Advanced Alchemy auto creates db_session dependency so you can remove dependencies={'transaction': provide_transaction}.
So you can just do

@get("/my-endpoint")
async def my_controller(db_session: AsyncSession) -> str:
    # Access the database session here.
    return "Hello, World!"
crisp dock
#

Good to know for my routes, but my question is regarding middleware, where you cannot access dependencies at all.

sharp escarp
crisp dock
#

Yes, but to access either of those, you need to reference your instance of the config that you passed into the app.

sharp escarp
#

You can also access plugins from app

#

scope["app"].plugins.get("litestar.channels.plugin.<plugin_name>")

#

You can print the scope["app"].plugins and find your alchemy config there

crisp dock
#

Looks like using that, I can access: connection.scope['app'].plugins.get('advanced_alchemy.extensions.litestar.plugins.init.plugin.SQLAlchemyInitPlugin') but then I would still need to find where it stores provide_transaction. Seems like at that point, I'd be better off storing a reference like I am currently, or factoring out the config to its own Python module.

sharp escarp
#
alchemy = connection.scope['app'].plugins.get('advanced_alchemy.extensions.litestar.plugins.init.plugin.SQLAlchemyInitPlugin')
alchemy.provide_session(state, scope)

Does this not work for you?

crisp dock
#

No: AttributeError: 'SQLAlchemyInitPlugin' object has no attribute 'provide_transaction'

sharp escarp
#

try with provide_session

crisp dock
#

Oops

#

AttributeError: 'SQLAlchemyInitPlugin' object has no attribute 'provide_session'

sharp escarp
#

hmm one sec

#

Ohh my bad you need SQLAlchemyAsyncConfig and not the plugin

#

I suggest you refactor your application so you can import your alchemy config, if that's not possible you can import alchemy config inside your middleware as well to prevent cyclic import