FastAPI Blueprint 03: Core FastAPI Structure
Learn how to structure FastAPI apps for flexibility so you can plug in new features without breaking the system.

What You’ll Learn in This Chapter
By now, you have:
- A working FastAPI app scaffold from Lesson 1
- A clean environment and Docker setup from Lesson 2
In this chapter, we’ll:
- Structure the app for modularity
- Organize routes into versioned modules
- Implement shared dependencies
- Prepare the app for future features (auth, databases, Kafka) without rewriting core logic
By the end, you’ll have a production-ready FastAPI skeleton that’s easy to extend for any microservice.
Why Structure Matters in FastAPI
FastAPI is flexible, but if you throw all routes into main.py
, your project will:
- Get messy quickly
- Make it harder to reuse code
- Slow down onboarding for new devs
We’ll fix that by:
- Grouping routes into versioned modules
- Using dependency injection for reusability
- Keeping
main.py
minimal (just an app factory)
Folder Structure Update
We’ll refine from Lesson 2 to introduce domain-driven structure:
app/
├─ api/
│ ├─ deps.py # Shared dependencies
│ ├─ v1/
│ │ ├─ __init__.py
│ │ ├─ routes_health.py # System health/version
│ │ ├─ routes_users.py # Example resource
│ │ └─ routes_items.py # Example resource
├─ core/
│ ├─ config.py
│ ├─ logging.py
│ ├─ version.py
├─ domain/ # Business logic
│ ├─ users.py
│ └─ items.py
├─ ports/
├─ adapters/
└─ main.py
Step-by-Step Build
1. Shared Dependencies
Dependencies help avoid repetitive code like DB connections or auth checks.
app/api/deps.py
from fastapi import Depends
# Example: current user dependency placeholder
def get_current_user():
return {"username": "demo_user"}
2. Routes by Feature
We’ll create an example resource to show modularity.
app/api/v1/routes_users.py
from fastapi import APIRouter, Depends
from app.api.deps import get_current_user
router = APIRouter()
@router.get("/users/me", tags=["users"])
async def read_current_user(current_user: dict = Depends(get_current_user)):
return current_user
app/api/v1/routes_items.py
from fastapi import APIRouter
router = APIRouter()
@router.get("/items", tags=["items"])
async def list_items():
return [{"id": 1, "name": "Example Item"}]
3. API Router Assembly
We’ll gather all v1 routes in a single file for cleaner main.py
.
app/api/v1/init.py
from fastapi import APIRouter
from .routes_health import router as health_router
from .routes_users import router as users_router
from .routes_items import router as items_router
api_router = APIRouter()
api_router.include_router(health_router)
api_router.include_router(users_router)
api_router.include_router(items_router)
4. Clean main.py
Your main.py
now just wires settings, logging, and routes.
app/main.py
from fastapi import FastAPI
from app.core.config import settings
from app.core.logging import setup_logging
from app.api.v1 import api_router
def create_app() -> FastAPI:
setup_logging(settings.log_level)
app = FastAPI(
title=settings.app_name,
version="0.1.0",
docs_url="/docs",
redoc_url="/redoc",
)
app.include_router(api_router, prefix=settings.api_v1_str)
return app
app = create_app()
5. Test It
Run locally:
poetry run uvicorn app.main:app --reload
Test endpoints:
curl http://127.0.0.1:8000/api/v1/health
curl http://127.0.0.1:8000/api/v1/users/me
curl http://127.0.0.1:8000/api/v1/items
Checklist for Chapter 3 Completion
api/v1
folder contains separate route files for each featuredeps.py
created for shared dependenciesapi_router
collects all routes into one import formain.py
main.py
contains no business logic — just app wiring- App runs and all endpoints respond correctly
Next Up – Lesson 4
In FastAPI Blueprint 04: Database Layer Abstraction, we’ll:
- Implement a generic database port interface
- Create adapters for PostgreSQL, MongoDB, and Redis
- Show how to switch DBs via config, even run multiple in one service