Code after yield in the async dependecy executed before response has been sent to the client
See original GitHub issueFirst Check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn’t find it.
- I searched the FastAPI documentation, with the integrated search.
- I already searched in Google “How to X in FastAPI” and didn’t find any information.
- I already read and followed all the tutorial in the docs and didn’t find an answer.
- I already checked if it is not related to FastAPI but to Pydantic.
- I already checked if it is not related to FastAPI but to Swagger UI.
- I already checked if it is not related to FastAPI but to ReDoc.
Commit to Help
- I commit to help with one of those options 👆
Example Code
import asyncio
from typing import AsyncGenerator
import structlog
import uvicorn
from fastapi import FastAPI, Depends
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.responses import Response
logger = structlog.getLogger(__name__)
app = FastAPI()
class BarMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
logger.info("BAR: Before call_next()")
response = await call_next(request)
logger.info("BAR: After call_next()")
logger.info(f"BAR: Status code {response.status_code}")
logger.info("BAR: Before sleep()")
await asyncio.sleep(2)
logger.info("BAR: After sleep()")
return response
class ZxcMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
logger.info("ZXC: Before call_next()")
response = await call_next(request)
logger.info("ZXC: After call_next()")
logger.info(f"ZXC: Status code {response.status_code}")
logger.info("ZXC: Before sleep()")
await asyncio.sleep(2)
logger.info("ZXC: After sleep()")
return response
async def get_foo() -> AsyncGenerator[str, None]:
logger.info("BEFORE YIELD FOO")
yield "foo"
logger.info("AFTER YIELD FOO")
async def get_qwerty(foo: str = Depends(get_foo)) -> AsyncGenerator[str, None]:
logger.info("BEFORE YIELD QWERTY")
yield "qwerty"
logger.info("AFTER YIELD QWERTY")
@app.get("/")
async def root(qwerty: str = Depends(get_qwerty)):
return {"message": qwerty}
app.add_middleware(BarMiddleware)
app.add_middleware(ZxcMiddleware)
if __name__ == "__main__":
uvicorn.run(
"test:app",
host="0.0.0.0",
port=8080,
reload=True,
)
Description
Hi.
From the documentation:
The code following the yield statement is executed after the response has been delivered:
But this is not the case:
2022-06-23 09:21:29,932 uvicorn.error INFO: Finished server process [30197]
2022-06-23 09:21:30,309 uvicorn.error INFO: Started server process [30216]
2022-06-23 09:21:30,309 uvicorn.error INFO: Waiting for application startup.
2022-06-23 09:21:30,309 uvicorn.error INFO: Application startup complete.
2022-06-23 09:22.12 [info ] ZXC: Before call_next()
2022-06-23 09:22.12 [info ] BAR: Before call_next()
2022-06-23 09:22.12 [info ] BEFORE YIELD FOO
2022-06-23 09:22.12 [info ] BEFORE YIELD QWERTY
2022-06-23 09:22.12 [info ] BAR: After call_next()
2022-06-23 09:22.12 [info ] BAR: Status code 200
2022-06-23 09:22.12 [info ] BAR: Before sleep()
2022-06-23 09:22.14 [info ] BAR: After sleep()
2022-06-23 09:22.14 [info ] ZXC: After call_next()
2022-06-23 09:22.14 [info ] ZXC: Status code 200
2022-06-23 09:22.14 [info ] ZXC: Before sleep()
2022-06-23 09:22.14 [info ] AFTER YIELD QWERTY
2022-06-23 09:22.14 [info ] AFTER YIELD FOO
2022-06-23 09:22.16 [info ] ZXC: After sleep()
2022-06-23 09:22:16,494 uvicorn.access INFO: 127.0.0.1:55382 - "GET / HTTP/1.1" 200
Response has not been sent to the client yet, but we’re already in the “after yield” code. I can not reproduce it with a single middleware though.
Using native ASGI middlewares (instead of inheriting from BaseHTTPMiddleware) does not solve the issue.
It’s a problem for us, because we can not rely on the fact that transaction has been commited already (in the middleware), when we’re in the “after yield” code.
Either docs are misleading, or something works incorrectly.
Operating System
Linux
Operating System Details
No response
FastAPI Version
0.78.0
Python Version
3.8.10
Additional Context
No response
Issue Analytics
- State:
- Created a year ago
- Reactions:3
- Comments:9 (6 by maintainers)
Top Results From Across the Web
Dependencies with yield - FastAPI
The code following the yield statement is executed after the response has been delivered: async def get_db(): db = DBSession() try: yield db...
Read more >Durable Orchestrations - Azure Functions | Microsoft Learn
Orchestrator functions have the following characteristics: ... until the function code is finished or until it has scheduled new async work.
Read more >Web Server Advanced — aiohttp 3.8.3 documentation
The attribute is a list of asynchronous generators, a code before yield is an initialization stage (called on startup), a code after yield...
Read more >Event Loop — Python 3.11.1 documentation
Source code: Lib/asyncio/events.py, Lib/asyncio/base_events.py Preface The event loop is the core of every asyncio application. Event loops run asynchronous ...
Read more >How to return error response when database failed to commit
But as i know, from 0.74.0 , dependencies with yield can catch HTTPException and custom exceptions before response was sent(i test 0.80.0 is...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Fair point, I corrected my comment above 😇
That’s not for sure yet… There has been a lot of discussion about
BaseHTTPMiddleware
. Also, the limitations ofBaseHTTPMiddleware
are usually not a problem for most of the users.That being said… They are still limitations, and if you are going to have them, for the time being, pure ASGI middlewares are recommended.