In this tutorial, we’ll break down the anatomy of a scalable Python project using FastAPI and uv. The goal is to create a clean and maintainable structure that makes it easy to collaborate, onboard new developers, and deploy with confidence.
We’ll cover:
- Setting up a clean folder structure
- Managing configuration with
.env
- Adding centralized logging
- Writing simple but effective tests
- Using
pyproject.toml
and.python-version
for consistency - Containerizing with Docker and orchestrating with docker-compose
1. Project Setup
First, let’s create a new directory for our project.
mkdir fastapi-scalable
cd fastapi-scalable
We’ll use uv as our package manager.
uv init
This creates a pyproject.toml
file, which will manage dependencies and project metadata.
2. Folder Structure
A good folder structure makes your project easy to navigate and extend.
fastapi-scalable/
│── app/
│ ├── api/
│ │ ├── v1/
│ │ │ └── routes.py
│ ├── core/
│ │ ├── config.py
│ │ ├── logging.py
│ ├── models/
│ │ └── user.py
│ ├── services/
│ │ └── user_service.py
│ └── main.py
│
│── tests/
│ ├── test_routes.py
│
│── .env
│── .python-version
│── pyproject.toml
│── Dockerfile
│── docker-compose.yml
3. Managing Configuration with .env
Centralize your environment variables in a .env
file.
.env
APP_NAME=FastAPI Scalable Project
APP_ENV=development
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
Load these variables using pydantic-settings in app/core/config.py
:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str
app_env: str
database_url: str
class Config:
env_file = ".env"
settings = Settings()
4. Centralized Logging
Logging helps track errors and monitor the health of your app.
app/core/logging.py
:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger("fastapi-scalable")
Usage in routes:
from fastapi import APIRouter
from app.core.logging import logger
router = APIRouter()
@router.get("/")
def read_root():
logger.info("Root endpoint accessed")
return {"message": "Welcome to FastAPI Scalable Project"}
5. Writing Tests
A simple test using pytest.
tests/test_routes.py
:
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Welcome to FastAPI Scalable Project"}
Run tests:
pytest
6. Using pyproject.toml
and .python-version
Your pyproject.toml
defines dependencies:
[project]
name = "fastapi-scalable"
version = "0.1.0"
dependencies = [
"fastapi",
"uvicorn",
"pydantic-settings",
"pytest",
]
[tool.uv]
python = "3.11"
.python-version
:
3.11
This ensures consistency across environments.
7. Containerization with Docker
Dockerfile
:
FROM python:3.11-slim
WORKDIR /app
COPY pyproject.toml .
COPY uv.lock .
RUN pip install uv && uv sync
COPY . .
CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml
:
version: "3.9"
services:
web:
build: .
ports:
- "8000:8000"
env_file: .env
volumes:
- .:/app
depends_on:
- db
db:
image: postgres:14
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
ports:
- "5432:5432"
Run with:
docker-compose up --build
Tip
You now have a scalable FastAPI project structure with configuration management, centralized logging, testing, and containerization. This setup makes it easy to extend features, onboard teammates, and deploy in consistent environments.
Top comments (0)