I am having a class to send notifications. When being initialised, it involves making a connection to a notification server, which is time-consuming. I use a background task in FastAPI to send notifications, as I don't want to delay the response due to notification. Below is the sample code.
file1.py:
noticlient = NotificationClient()
@app.post("/{data}")
def send_msg(somemsg: str, background_tasks: BackgroundTasks):
result = add_some_tasks(data, background_tasks, noticlient)
return result
file2.py:
def add_some_tasks(data, background_tasks: BackgroundTasks, noticlient):
background_tasks.add_task(noticlient.send, param1, param2)
result = some_operation
return result
Here, notification client is declared globally. I could have it initialised in file2.py
under add_some_tasks
, but it would get initialised every time a request arrives, and that would require some time. Is there any way to use a middleware to re-use it every time a request arrives, so that it doesn' t need to be initialised every time.
or Approach two: Initialize notification in class def
file1.py:
class childFastApi(FastAPI):
noticlient = NotificationClient()
app = childFastApi()
@app.post("/{data}")
def send_msg(somemsg: str, background_tasks: BackgroundTasks):
result = add_some_tasks(data, background_tasks, app.noticlient)
return result
You could store the custom class object to the app instance, which allows you to store arbitrary extra state using the generic the app.state
attribute, as demonstrated here, as well as here and here. To access the app.state
attribute, and subsequently the object, outside the main file (for instance, from a routers
submodule that uses APIRouter
), you could use the Request
object, as demonstrated in this answer (i.e., using request.app.state
). You could either use a startup
event (as shown here) to initialise the object, but since it is now deprecated (and might be removed in future versions), you could instead use a lifespan
function.
from fastapi import FastAPI, Request
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
''' Run at startup
Initialise the Client and add it to app.state
'''
app.state.n_client = NotificationClient()
yield
''' Run on shutdown
Close the connection
Clear variables and release the resources
'''
app.state.n_client.close()
app = FastAPI(lifespan=lifespan)
@app.get('/')
async def main(request: Request):
n_client = request.app.state.n_client
# ...
Since the introduction of Starlette's lifespan
handler, which, similar to startup
and shutdown
events, allows one to define code that needs to run before the application starts up, or when the application is shutting down, one could also define objects to be accesible from the request.state
. As per Starlette's documentation:
The
lifespan
has the concept ofstate
, which is a dictionary that can be used to share the objects between the lifespan, and the requests.The
state
received on the requests is a shallow copy of the state received on the lifespan handler.
Hence, after instantiating the class object in the lifespan handler, you could then add it to the dictionary (i.e., the state
), and access it within endpoints—even those defined in APIRouter
s outside the main application file— using request.state
.
from fastapi import FastAPI, Request
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
''' Run at startup
Initialise the Client and add it to request.state
'''
n_client = NotificationClient()
yield {'n_client': n_client}
''' Run on shutdown
Close the connection
Clear variables and release the resources
'''
n_client.close()
app = FastAPI(lifespan=lifespan)
@app.get('/')
async def main(request: Request):
n_client = request.state.n_client
# ...