1

I've been migrating an existing WSGI app to FastAPI. I'm having trouble fully understanding the different dynamics of the two frameworks, and hope you can shed some light conceptually.

At the moment I have the following setup, largely inspired by existing app:

  • ASGI middleware that catches exceptions
  • ASGI middleware that parses request user credentials and stores result in a threading.local() object
  • ASGI middleware that opens database session and catches database exceptions in subsequent logic
  • Simple (async) route handler for legacy routes, which translates Starlette request args into format accepted by former app, then calling the route synchronously.

All legacy endpoints "work". Meaning: results were functionally correct in a unit test setting and simple localhost testing. I noticed something was amiss when I added a 'native' sync route: it was executed in a separate (AnyIO) thread from the middleware, and therefore it did not have the user credentials it parsed.

Some initial questions, feel free to add/tell me if I'm asking the wrong question:

  • Currently sync legacy routes (with database I/O) are run in an async route handler. I assume that's a bad idea from a performance standpoint?
  • Is threading.local() even local to individual async routes? Is there an 'intended' way in FastAPI/Starlette for ASGI middleware to provide data to the function that ultimately executes the request?
  • FastAPI's database example uses a dependency with a generator to open the database session. When trying this setup, I noticed database issues upon commit (done by the same dependency after the yield) would still result in an OK response sent to the user. Could this be correct? If so, must I explicitly commit within every route function (or wrap it in a decorator that does so) instead of the dependency?
  • I noticed FastAPI's add_exception_handler() doesn't catch exceptions in ASGI middleware. Is that correct?
  • Which other questions should I be asking here?

Insights are welcome - thanks :)

expatriate
  • 41
  • 4
  • No, `threading.local()` is not local to each route. Look into [contextvars](https://docs.python.org/3/library/contextvars.html) – M.O. Dec 10 '22 at 11:27

0 Answers0