I am a bit confused about best practices when it comes to where and how to initiate the Flask() or FastAPI() for a data-intensive web API. The vast majority of the code available online is using this structure:
app.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
But based on hackersandslackers.com article, this does not seems a good practice:
Notice there's no
app.py,main.py, or anything of the sort in our base directory. Instead, the entirety of our app lives in the/applicationfolder, with the creation of our app happening in__init__.py. The init file is where we actually create what's called the Application Factory. If you're wondering how we deploy an app where the main entry point isn't in the root directory, I'm very proud of you. Yes, our app is being created inapplication/__init__.py, so a file calledwsgi.pysimply imports this file to serve as our app gateway. More on that another time.
# The Flask Application Factory pattern
/app
├── /application
│ ├── __init__.py
│ ├── auth.py
│ ├── forms.py
│ ├── models.py
│ ├── routes.py
│ ├── /static
│ └── /templates
├── config.py
└── wsgi.py
So I did follow hackersandslackers suggestions but I also did a couple of modifications to implement the Factory Pattern on their design. This is my app structure:
src/__ init __.py
from fastapi import FastAPI
from src.api.v1.village import village_router
from src.api.v1.auth import auth_router
from src.core.config import *
def create_app() -> FastAPI:
root_app = FastAPI()
root_app.include_router(
village_router,
prefix="/api/v1",
tags=["village"],
)
return root_app
src/asgi.py
import uvicorn
from src import create_app
app = create_app()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", log_level="info", reload=True)
And to run my app I have to type uvicorn asgi:app.
Is that a bad practice to have my create_app() in the __init__.py?
__init__.pyshould be avoided, or whether Flask and FastAPI applications should prefer one or the other structure.__init__.pyis that you can easily run into circular import issues, because that code will be automatically imported whenever some other code imports something within that package. If__init__.pydepends on something within the same package, by importing it, and other code within the same package also imports something from the same package, you can easily get a circular import error.