Initialize FastAPI backend projects with UV package manager, configure project structure, install dependencies, and set up basic FastAPI application...
Quick reference for initializing FastAPI backend projects with modern tooling (UV, SQLModel, pytest).
cd backend
# Initialize Python project with UV
uv init --name backend --python 3.13
# Create project structure
mkdir -p src/models src/routers src/services src/middleware src/schemas src/utils tests alembic
# Create __init__.py files
touch src/__init__.py
touch src/models/__init__.py
touch src/routers/__init__.py
touch src/services/__init__.py
touch src/middleware/__init__.py
touch src/schemas/__init__.py
touch src/utils/__init__.py
touch tests/__init__.py
# Core dependencies
uv add fastapi[all]
uv add sqlmodel
uv add psycopg2-binary
uv add python-jose[cryptography]
uv add passlib[bcrypt]
uv add python-dotenv
uv add alembic
# Development dependencies
uv add --dev pytest
uv add --dev pytest-cov
uv add --dev httpx
uv add --dev pytest-asyncio
Create src/main.py:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import os
from dotenv import load_dotenv
load_dotenv()
app = FastAPI(
title="Todo API",
description="RESTful API for Todo Web Application - Phase 2",
version="1.0.0"
)
# CORS configuration
origins = [
os.getenv("FRONTEND_URL", "http://localhost:3000"),
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def root():
return {"message": "Todo API is running", "version": "1.0.0"}
@app.get("/health")
async def health_check():
return {"status": "healthy"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Create src/config.py:
from pydantic_settings import BaseSettings
from functools import lru_cache
class Settings(BaseSettings):
# Database
DATABASE_URL: str
# Authentication
BETTER_AUTH_SECRET: str
JWT_ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_DAYS: int = 7
# CORS
FRONTEND_URL: str = "http://localhost:3000"
# Environment
ENVIRONMENT: str = "development"
class Config:
env_file = ".env"
case_sensitive = True
@lru_cache()
def get_settings():
return Settings()
settings = get_settings()
Create .env.example:
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# Authentication
BETTER_AUTH_SECRET=your-secret-key-here-min-32-characters
# CORS
FRONTEND_URL=http://localhost:3000
# Environment
ENVIRONMENT=development
Create actual .env file:
cp .env.example .env
# Edit .env with actual values
Create src/database.py:
from sqlmodel import create_engine, Session, SQLModel
from sqlalchemy.pool import NullPool
from src.config import settings
engine = create_engine(
settings.DATABASE_URL,
echo=settings.ENVIRONMENT == "development",
poolclass=NullPool, # Let Neon handle pooling
connect_args={
"connect_timeout": 10,
"options": "-c timezone=utc"
}
)
def create_db_and_tables():
"""Create all database tables"""
SQLModel.metadata.create_all(engine)
def get_session():
"""FastAPI dependency for database sessions"""
with Session(engine) as session:
yield session
# Initialize Alembic
alembic init alembic
# Edit alembic.ini - comment out the sqlalchemy.url line
# It will be set from environment variable
# Edit alembic/env.py
Update alembic/env.py:
from logging.config import fileConfig
from sqlmodel import SQLModel
from sqlalchemy import engine_from_config, pool
from alembic import context
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Import all models so Alembic can detect them
from src.models.task import Task # Import as you create models
# Alembic Config object
config = context.config
# Set DATABASE_URL from environment
config.set_main_option("sqlalchemy.url", os.getenv("DATABASE_URL"))
# Interpret the config file for Python logging
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# Set target metadata for autogenerate
target_metadata = SQLModel.metadata
# ... rest of env.py (keep default functions)
Create tests/conftest.py:
import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, create_engine, SQLModel
from sqlmodel.pool import StaticPool
from src.main import app
from src.database import get_session
@pytest.fixture(name="session")
def session_fixture():
engine = create_engine(
"sqlite:///:memory:",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
yield session
@pytest.fixture(name="client")
def client_fixture(session: Session):
def get_session_override():
return session
app.dependency_overrides[get_session] = get_session_override
client = TestClient(app)
yield client
app.dependency_overrides.clear()
Create tests/test_main.py:
def test_root(client):
response = client.get("/")
assert response.status_code == 200
assert "message" in response.json()
def test_health_check(client):
response = client.get("/health")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
Create or update pyproject.toml:
[project]
name = "backend"
version = "1.0.0"
description = "Todo API Backend - Phase 2"
requires-python = ">=3.13"
dependencies = [
"fastapi[all]>=0.115.0",
"sqlmodel>=0.0.24",
"psycopg2-binary>=2.9.9",
"python-jose[cryptography]>=3.3.0",
"passlib[bcrypt]>=1.7.4",
"python-dotenv>=1.0.0",
"alembic>=1.13.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-cov>=4.1.0",
"httpx>=0.26.0",
"pytest-asyncio>=0.23.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v --cov=src --cov-report=term-missing"
# Run the FastAPI server
cd backend
uv run uvicorn src.main:app --reload --port 8000
# In another terminal, run tests
uv run pytest
# Check code coverage
uv run pytest --cov=src --cov-report=html
After setup, verify:
uv run uvicorn src.main:app --reload starts successfullyuv run pytest runs and passes.env file exists and has DATABASE_URLAfter basic setup:
src/models/src/routers/UV not found:
curl -LsSf https://astral.sh/uv/install.sh | sh
Import errors:
uv run python instead of just pythonDatabase connection errors: