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 Python project with the following file structure:
  2. 
    ├── app
    │   │── env
    │   ├── __init__.py
    │   ├── main.py
    │   └── src
    │   │   ├── __init__.py
    │   │   ├── user
    │   │   │    ├── __init__.py
    │   │   │    └── main.py
    │   │   │
    │   │   └── item 
    │   │         ├── __init__.py
    │   │         └── main.py
    

    There are user and item folders inside the src folder. The user folder has a main.py file for user APIs. Similarly, the item folder has a main.py file for item related 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.

  3. Install FastAPI dependency using the command:
  4. 
    pip install fastapi
    
  5. Next, install uvicorn. Uvicorn is an implementation of ASGI server for fast performance. To install uvicorn, use the command:
  6. 
    pip install uvicorn
    
  7. Create user related APIs in the src/user/main.py file:
  8. 
    from fastapi import APIRouter, Depends, HTTPException
    from pydantic import BaseModel, Field
    from typing import Optional
    from fastapi import Query
    
    #APIRouter creates path operations for user module
    router = APIRouter(
        prefix="/users",
        tags=["users"],
        responses={404: {"description": "Not found"}},
    )
    
    class User(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")
    
    @router.get("/")
    async def read_root():
        return {"Hello": "World"}
    
    @router.get("/{user_id}")
    async def read_user(user_id: int):
        return {"user_id": user_id, "full_name": "Danny Manny", "email": "danny.manny@gmail.com"}
    
    @router.get("/detail")
    async def read_users(q: Optional[str] = Query(None, max_length=50)):
        results = {"users": [{"user_id": 1}, {"user_id": 2}]}
        if q:
            results.update({"q": q})
        return results
    
    @router.post("/add")
    async def add_user(user: User):
        return {"full_name": user.first_name+" "+user.last_name}
    
    @router.put("/update")
    async def read_user(user: User):
        return {"user_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 {"user_id": user_id, "is_deleted": True}
    
  9. Create APIs for item in the src/item/main.py file:
  10. 
    from fastapi import APIRouter, Depends, HTTPException
    from pydantic import BaseModel, Field
    from typing import Optional
    from fastapi import Query
    
    #APIRouter creates path operations for item module
    router = APIRouter(
        prefix="/items",
        tags=["items"],
        responses={404: {"description": "Not found"}},
    )
    
    
    class Item(BaseModel):
        item_id: Optional[int] = None
        name: str
        code: str
        description: Optional[str] = Field(
            None, title="The description of the item", max_length=300
        )
        price: float = Field(..., gt=0,
                             description="The price must be greater than zero")
    
    @router.get("/")
    async def read_root():
        return {"Hello": "item"}
    
    @router.get("/{item_id}")
    async def read_item(item_id: int):
        return {"item_id": item_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 = {"item": [{"item_id": 1}, {"item_id": 2}]}
        if q:
            results.update({"q": q})
        return results
    
    @router.post("/add")
    async def add_item(item: Item):
        return {"name": item.name+", code:"+item.code}
    
    @router.put("/update")
    async def update_item(item: Item):
        return {"item_id": item.item_id, "name": item.name, "code":item.code, "price": item.price}
    
    @router.delete("/{item_id}/delete")
    async def delete_item(item_id: int):
        return {"item_id": item_id, "is_deleted": True}
    
  11. On the app/main.py file add the code as shown in the example below:
  12. 
    import uvicorn
    from fastapi import FastAPI
    from src.user import main as user_main
    from src.item import main as item_main
    
    app = FastAPI()
    
    app.include_router(user_main.router)
    app.include_router(item_main.router)
    
    if __name__ == '__main__':
        uvicorn.run(app, host='127.0.0.1', port=8005)
        print("running")
    
  13. Run your application and open http://127.0.0.1:8005/docs on your web browser.