Description
Hi. First of all great work with fastapi. I am currently evaluating shifting one of my api gateway from sanic / aiohttp to using fastapi / aiohttp. I have a bunch of microservices exposing various rest / grpc apis. The aim of the api gateway (with fast api) is to front all of them and provide a nice versioned, documented api contract. A typical api route can send requests to different endpoints and integrate results to formulate a nice response from the api gateway.
How can I [...]?
@app.on_event("startup")
. Is it possible to add those for every apirouter rather than the main app. If so how can clientSession objects be available inside routes to do request?Is it possible to [...]?
If all of this is not supported what would be the best approach to move forward.
Additional context
Add any other context or screenshots about the feature request here.
@faheem-cliqz you can just create a ClientSession
object in a module (a Python file) and import it everywhere, you don't need to create the session on startup
.
If you need to use different clients (one per router or similar) you can create several sessions in that external module. Or even several external modules each one with a session (if you want).
If you want to close those sessions properly, you can import those client sessions in your main file and close them in the shutdown
event.
The important thing is, you don't have to create the sessions in the startup
event. You can create them outside. So, you don't have to fight with circular imports.
@tiangolo Yup that is pretty much what I am doing at the moment. Thought it was useful to ask, if there is a best practice surrounding this in fastapi.
Cool! Thanks for reporting back and closing the issue.
I apologize in advance for commenting on a closed issue, but this is exactly I am looking for.
@faheem-cliqz @tiangolo - May I please request an example of this? I am using Postgresql starter project for my project.
Hi @legendactivated , if you would like to move to async on postregsql starter kit, you will probably find this starter code interesting: https://gitlab.com/euri10/euri10_fastapi_base
It comes from @euri10 who is deeply involved in fastapi 馃
Got it thanks!! Will check it out
Thanks @ebreton for your help! :cake: :rocket:
Since I did not found any complete resource for that , I did a full example of :
fastAPI with an aiohttp client
full code and tests examples :
Thanks for the example, I solved it sometime ago, should have posted a solution here. The trick is to start your clientsession in the eventloop and able to handle it in fastapi server start/stop events is a bonus. There is some nice explanation in aiohttp faqs as well https://aiohttp.readthedocs.io/en/stable/faq.html#why-is-creating-a-clientsession-outside-of-an-event-loop-dangerous .
So singletons are the way?
It's one way to do :
If you only want open one session by fastAPI instance/worker and do not need to refresh it
If you still want keep only one session , but need to refresh it every X seconds ( ex : there is a loadbalancer between you and the endpoint) , you can put that refresh logic ( red/black session ) inside a TASK that you add to the asyncio.running_loop
Can't you also model this by using the dependency mechanism to inject a singleton aiohttp ClientSession instance?
@wichert something like this?
from fastapi import FastAPI, Depends
import aiohttp
from typing import Any, Dict, List, Union
app = FastAPI()
JSONType = Union[str, int, float, bool, None, Dict[str, Any], List[Any]]
class AioWrap(object):
async def __call__(self, site: str) -> JSONType:
async with aiohttp.ClientSession() as client:
async with client.get(site) as resp:
return await resp.json()
@app.post("/")
async def scrape_any_site(site: str, client: AioWrap = Depends(AioWrap)):
return await client(site)
That is one way if you want create a new session for every request. You can also use a singleton approach:
class HttpClient:
session: aiohttp.ClientSession = None
def start(self):
self.session = aiohttp.ClientSession()
async def stop(self):
await self.session.close()
self.session = None
def __call__(self) -> aiohttp.ClientSession:
assert self.session is not None
return self.session
http_client = HttpClient()
@app.on_event("startup")
async def startup():
http_client.start()
@app.get("/fancy-path")
async def get_something(http_client: aiohttp.ClientSession = Depends(http_client)):
r = await http_client.get("https://api.exampe.com")
return r.json()
Most helpful comment
Since I did not found any complete resource for that , I did a full example of :
fastAPI with an aiohttp client
full code and tests examples :
https://github.com/raphaelauv/fastAPI-aiohttp-example