Fastapi: [QUESTION] aiohttp integration best practice

Created on 16 May 2019  路  14Comments  路  Source: tiangolo/fastapi

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 [...]?

  • My questions revolve around best way to use aiohttp client within routes driven through an apirouter?
  • Best way to initialize aiohttp ClientSession outside the routes as they provide a pool of tcp connections? There could be multiple clientSessions i.e. per type of microservice to provide consistency in timeouts dns behavior etc.
  • Sessions should be initialized before app start. I see support for events i.e. @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.

question

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

All 14 comments

@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 :

https://github.com/raphaelauv/fastAPI-aiohttp-example

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()
Was this page helpful?
0 / 5 - 0 ratings