A modern FastAPI application demonstrating async database operations with SQLAlchemy 2.0 and SQLite.
- Async/Await: Full async support with FastAPI and SQLAlchemy 2.0
- Modern SQLAlchemy: Using
async_sessionmakerandcreate_async_engine - Auto-generated API Docs: Interactive OpenAPI/Swagger documentation
- Clean Architecture: Separated concerns with models, schemas, and routers
- Database Migrations: Automatic table creation and seeding
- Type Safety: Full type hints and Pydantic validation
- Python 3.11+
- Virtual environment (recommended)
# Set Python version
pyenv shell 3.11
# Create and activate virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Optional: upgrade to latest versions
pip install --upgrade $(pip freeze | cut -d '=' -f 1)# Start with database reset and seed data
RESET_DB=true uvicorn app.main:app --reload
# Start without resetting database
uvicorn app.main:app --reloadOpen your browser to: http://127.0.0.1:8000/docs
You can run this FastAPI app in a container using Docker. This is the recommended way for consistent deployment and local development.
docker build -t fastapi-todo-app-async .docker run --rm -p 8000:8000 \
-e RESET_DB=true \
--name fastapi-todo-app-async \
fastapi-todo-app-asyncThis will start the app and expose it at http://localhost:8000.
- Mount your code for live reload (optional):
docker run --rm -p 8000:8000 \ -v $(pwd)/app:/app/app \ -e RESET_DB=true \ fastapi-todo-app - Use environment variables to control database reset and configuration.
- For production, remove
RESET_DB=trueand consider using a production-ready server like Gunicorn with Uvicorn workers.
TODO: Docker Compose with external database support
- FastAPI 0.115.2 - Modern async web framework
- SQLAlchemy 2.0.36 - Async ORM with latest patterns
- SQLite - File-based database with async support via
aiosqlite - Pydantic - Data validation and serialization
- Passlib - Password hashing with bcrypt
app/
βββ main.py # FastAPI app initialization
βββ db.py # Async database configuration
βββ db_migration.py # Database initialization and seeding
βββ models.py # SQLAlchemy ORM models
βββ schemas.py # Pydantic validation schemas
βββ crud_todo.py # Todo CRUD operations
βββ crud_user.py # User CRUD operations
βββ security.py # Password hashing utilities
βββ routers/
βββ users.py # User API endpoints
βββ todos.py # Todo API endpoints
The application uses SQLAlchemy 2.0 async patterns with:
create_async_engine()with connection poolingasync_sessionmaker()for session factory- Connection lifecycle management
- Automatic cleanup on shutdown
SQLALCHEMY_DATABASE_URL = "sqlite+aiosqlite:///./sqlite.db"Two approaches supported:
- Dependency Injection (FastAPI routers):
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
async with AsyncSessionLocal() as session:
yield session- Explicit Context Manager:
async with AsyncSessionLocal() as session:
# Your database operations
await session.commit()GET /api/users/- List all users (with optional filtering)GET /api/users/{user_id}- Get user by IDPOST /api/users/- Create new userPUT /api/users/{user_id}- Update userDELETE /api/users/{user_id}- Delete user
GET /api/todos/- List all todosGET /api/todos/{todo_id}- Get todo by IDPOST /api/todos/- Create new todoPUT /api/todos/{todo_id}- Update todoDELETE /api/todos/{todo_id}- Delete todo
The application supports dynamic query filtering using SQLAlchemy 2.0 syntax. Filters are built using ColumnElement expressions.
Two eager loading strategies implemented:
- joinedload: Uses JOIN queries for one-to-many relationships
- selectinload: Uses separate SELECT queries for many-to-one relationships
- Users have many Todos (one-to-many)
- Automatic cascade deletes
- UUID primary keys for better compatibility
# Manual dependency installation (if needed)
pip install sqlalchemy[asyncio] aiosqlite fastapi uvicorn pydantic[email] passlib[bcrypt]For different databases:
- PostgreSQL:
pip install asyncpg - MySQL:
pip install asyncmy - SQLite:
pip install aiosqlite(already included)
Set environment variables as needed:
export RESET_DB=true # Reset database on startup
export SQLALCHEMY_DATABASE_URL="postgresql+asyncpg://user:pass@localhost/dbname"- All database operations use async/await
- Proper session lifecycle management
- Connection pooling for optimal performance
- Graceful shutdown with connection cleanup
- Password hashing with bcrypt
- Email validation with Pydantic
- Input validation and sanitization
- No sensitive data in logs
- Full type hints throughout codebase
- Pydantic models for validation
- SQLAlchemy 2.0 type-safe queries
- MyPy compatible