Building Bigger FastAPI Applications with Multiple Separate Files in Python

  • Last updated Apr 25, 2024

We typically do not keep everything in a single file when building an application or web APIs. In this tutorial, you will learn how to organize code in separate files within a FastAPI project.

Here's a step-by-step guide on how to do this:

  1. Start by creating a new folder with the name of your project:
  2. mkdir project_name
  3. Navigate to your newly created project folder:
  4. cd project_name
  5. Create a virtual environment inside the project root directory using the following command:
  6. py -m venv env
    python3 -m venv env
  7. Activate the virtual environment using the command:
  8. .\env\Scripts\activate
    source env/bin/activate
  9. Install FastAPI:
  10. pip install fastapi
  11. Install uvicorn. Uvicorn is an implementation of ASGI server for fast performance. Use the following command to install uvicorn:
  12. pip install "uvicorn[standard]"
  13. Create a directory structure for your FastAPI project. A common structure should look like this:
  14. ├── project_name
    │   │── env
    │   ├── routes
    │   │     └── api.py
    │   ├── src
    │   │    ├── __init__.py
    │   │    ├── endpoints
    │   │    │      ├── __init__.py
    │   │    │      ├── product_service.py
    │   │    │      └── user_service.py
    │   │    │
    │   │    └── models
    │   │           ├── __init__.py
    │   │           ├── product.py
    │   │           └── user.py
    │   ├── __init__.py
    │   ├── main.py

    There are folders named models and endpoints inside the src folder. The models folder contains user.py and product.py files. Similarly, the endpoints folder contains user_service.py and product_service.py files with API endpoints.

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


  15. Create a model file named user.py in the src/models directory:
  16. 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")
  17. Create another model file named product.py in the src/models directory:
  18. 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")
  19. Create a service file named user_service.py in the src/endpoints directory:
  20. 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": "danny.manny@gmail.com"}
    
    @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}
  21. Create another service file named product_service.py in the src/endpoints directory:
  22. 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}
  23. Create a route file named api.py in the routes/ directory:
  24. from fastapi import APIRouter
    from src.endpoints import product_service, user_service
    
    router = APIRouter()
    router.include_router(product_service.router)
    router.include_router(user_service.router)
  25. Create a main file named main.py in the project's root directory:
  26. 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")
  27. Run the application:
  28. python main.py

    Run your application and open http://127.0.0.1:8005/docs in your web browser. You will see list of APIs that you can test.