FastAPI Bigger Applications with Multiple Separate Files in Python

We usually do not keep everything on a single file when we are building an application or web APIs. So, in this tutorial, we will learn how to put APIs on separate files in a FastAPI project.

Follow the steps below to complete this tutorial:

  1. Create a project folder and navigate to it via terminal:
  2. 
        mkdir project_name
        cd project_name
    
  3. Create a virtual environment inside the project root directory using the following command:
  4. 
       virtualenv env
    
  5. Activate the virtual environment using the command:
  6. On ubuntu/Linux/Mac
    
        source env/bin/activate
        
    On Windows
    
        env/Scripts/activate
        
  7. Install FastAPI dependency using the command:
  8. 
    pip install fastapi
    
  9. Next, install uvicorn. Uvicorn is an implementation of ASGI server for fast performance. To install uvicorn, use the command:
  10. 
    pip install uvicorn
    
  11. Create folders and files as shown below to make your project's file structure look like this:
  12. 
    ├── project_name
    │   │── env
    │   ├── routes
    │   │     └── api.py
    │   ├── src
    │   │    ├── __init__.py
    │   │    ├── endpoints
    │   │    │      ├── __init__.py
    │   │    │      ├── product.py
    │   │    │      └── user.py
    │   │    │
    │   │    └── models 
    │   │           ├── __init__.py
    │   │           ├── product.py
    │   │           └── user.py
    │   ├── __init__.py
    │   ├── main.py
    

    There are models and endpoints folders inside the src folder. The models folder has user.py and product.py files. Similarly, the endpoints folder has user.py and product.py files with APIs.

    There is one __init__.py file in every directory or subdirectory. This presence of __init__.py files allows importing code from one file into another.

  13. Create src/models/user.py file:
  14. 
    from pydantic import BaseModel, Field
    from typing import Optional
    
    class UserModel(BaseModel):
        user_id: Optional[int] = None
        first_name: str
        last_name: str
        email: str
        father_name: Optional[str] = Field(
            None, title="The father name of the User", max_length=300
        )
        age: float = Field(..., gt=0,
                           description="The age must be greater than zero")    
    
  15. Create src/models/product.py file:
  16. 
    from pydantic import BaseModel, Field
    from typing import Optional
    
    class ProductModel(BaseModel):
        item_id: Optional[int] = None
        name: str
        code: str
        description: Optional[str] = Field(
            None, title="The description of the Product", max_length=300
        )
        price: float = Field(..., gt=0,
                             description="The price must be greater than zero")
    
  17. Create user related APIs in the src/endpoints/user.py file:
  18. 
    from fastapi import APIRouter
    from fastapi import Query
    from src.models.user import UserModel
    from typing import Optional
    
    #APIRouter creates path operations for user module
    router = APIRouter(
        prefix="/users",
        tags=["User"],
        responses={404: {"description": "Not found"}},
    )
    
    @router.get("/")
    async def read_root():
        return [{"id": 1}, {"id": 2}]
    
    @router.get("/{user_id}")
    async def read_user(user_id: int):
        return {"id": user_id, "full_name": "Danny Manny", "email": "[email protected]"}
    
    @router.get("/detail")
    async def read_users(q: Optional[str] = Query(None, max_length=50)):
        results = {"users": [{"id": 1}, {"id": 2}]}
        if q:
            results.update({"q": q})
        return results
    
    @router.post("/add")
    async def add_user(user: UserModel):
        return {"full_name": user.first_name+" "+user.last_name}
    
    @router.put("/update")
    async def read_user(user: UserModel):
        return {"id": user.user_id, "full_name": user.first_name+" "+user.last_name, "email": user.email}
    
    @router.delete("/{user_id}/delete")
    async def read_user(user_id: int):
        return {"id": user_id, "is_deleted": True}
    
  19. Create APIs for item in the src/endpoints/product.py file:
  20. 
    from fastapi import APIRouter
    from typing import Optional
    from fastapi import Query
    from src.models.product import ProductModel
    
    #APIRouter creates path operations for item module
    router = APIRouter(
        prefix="/products",
        tags=["Product"],
        responses={404: {"description": "Not found"}},
    )
    
    @router.get("/")
    async def read_root():
        return [{"id":1,"product": "Laptop"}, {"id":2,"product": "Mobile"}]
    
    @router.get("/{product_id}")
    async def read_item(product_id: int):
        return {"id": product_id, "name": "Mobile", "code": "M12", "price": 200.0}
    
    @router.get("/detail")
    async def read_item_detail(q: Optional[str] = Query(None, max_length=50)):
        results = {"products": [{"id": 1}, {"id": 2}]}
        if q:
            results.update({"q": q})
        return results
    
    @router.post("/add")
    async def add_item(product: ProductModel):
        return {"name": product.name+", code:"+product.code}
    
    @router.put("/update")
    async def update_item(product: ProductModel):
        return {"id": product.item_id, "name": product.name, "code":product.code, "price": product.price}
    
    @router.delete("/{product_id}/delete")
    async def delete_item(product_id: int):
        return {"id": product_id, "is_deleted": True}
    
  21. Create routes/api.py file:
  22. 
    from fastapi import APIRouter
    from src.endpoints import product, user
    
    router = APIRouter()
    router.include_router(product.router)
    router.include_router(user.router)
    
  23. On the app/main.py file add the code as shown in the example below:
  24. 
    from imp import reload
    import uvicorn
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi import FastAPI
    from routes.api import router as api_router
    
    app = FastAPI()
    
    origins = ["http://localhost:8005"]
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=origins,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    app.include_router(api_router)
    
    if __name__ == '__main__':
        uvicorn.run("main:app", host='127.0.0.1', port=8005, log_level="info", reload=True)
        print("running")
    
  25. Run your application and go to http://127.0.0.1:8005/docs in your web browser. You will see list of APIs that you can test.