Managing FastAPI Projects with Poetry and TOML

Modern Python development has evolved significantly, and tools like Poetry and the TOML format have become essential for managing professional projects. In this guide, we'll explore how to use Poetry and TOML to manage FastAPI projects efficiently.
What is TOML?
TOML (Tom's Obvious, Minimal Language) is a configuration file format that's designed to be easy to read and write due to its obvious semantics. It maps unambiguously to a hash table, making it ideal for configuration files.
TOML vs Other Formats
# TOML - Clean and readable
[tool.poetry]
name = "my-fastapi-app"
version = "0.1.0"
description = "A modern FastAPI application"
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.109.0"// JSON - More verbose
{
"tool": {
"poetry": {
"name": "my-fastapi-app",
"version": "0.1.0",
"description": "A modern FastAPI application",
"dependencies": {
"python": "^3.11",
"fastapi": "^0.109.0"
}
}
}
}# YAML - Indentation-sensitive
tool:
poetry:
name: my-fastapi-app
version: 0.1.0
description: A modern FastAPI application
dependencies:
python: ^3.11
fastapi: ^0.109.0TOML combines the best of both worlds: JSON's explicitness and YAML's readability.
Why Poetry for FastAPI Projects?
Poetry is a dependency management and packaging tool that offers several advantages over traditional pip and requirements.txt:
1. Dependency Resolution
Poetry resolves dependencies automatically and prevents conflicts:
# Old way - manual conflict resolution
pip install fastapi==0.109.0
pip install pydantic==2.5.0 # Might conflict!
pip install sqlalchemy==2.0.0 # Another potential conflict!
# Poetry way - automatic resolution
poetry add fastapi pydantic sqlalchemy2. Separation of Concerns
Poetry distinguishes between production and development dependencies:
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.109.0"
uvicorn = {extras = ["standard"], version = "^0.27.0"}
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
black = "^23.12.1"
mypy = "^1.8.0"3. Lock File
poetry.lock ensures reproducible builds:
# Same versions on all machines
poetry install # Uses poetry.lock4. Virtual Environment Management
Poetry creates and manages virtual environments automatically:
# Poetry handles everything
poetry install
poetry run uvicorn main:app --reloadSetting Up Poetry for FastAPI
Installation
# Install Poetry (Unix/macOS)
curl -sSL https://install.python-poetry.org | python3 -
# Install Poetry (Windows PowerShell)
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
# Verify installation
poetry --versionCreating a New FastAPI Project
# Create new project
poetry new my-fastapi-app
cd my-fastapi-app
# Or initialize in existing directory
mkdir my-fastapi-app && cd my-fastapi-app
poetry initThe poetry init command will guide you through creating a pyproject.toml:
[tool.poetry]
name = "my-fastapi-app"
version = "0.1.0"
description = "A modern FastAPI application"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
license = "MIT"
[tool.poetry.dependencies]
python = "^3.11"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"Adding FastAPI Dependencies
# Add production dependencies
poetry add fastapi
poetry add "uvicorn[standard]"
poetry add sqlalchemy
poetry add pydantic-settings
poetry add python-jose[cryptography]
poetry add passlib[bcrypt]
poetry add python-multipart
# Add development dependencies
poetry add --group dev pytest
poetry add --group dev pytest-asyncio
poetry add --group dev httpx
poetry add --group dev black
poetry add --group dev ruff
poetry add --group dev mypyYour pyproject.toml will be updated:
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.109.0"
uvicorn = {extras = ["standard"], version = "^0.27.0"}
sqlalchemy = "^2.0.25"
pydantic-settings = "^2.1.0"
python-jose = {extras = ["cryptography"], version = "^3.3.0"}
passlib = {extras = ["bcrypt"], version = "^1.7.4"}
python-multipart = "^0.0.6"
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
pytest-asyncio = "^0.23.3"
httpx = "^0.26.0"
black = "^23.12.1"
ruff = "^0.1.11"
mypy = "^1.8.0"Understanding pyproject.toml Structure
Basic Metadata
[tool.poetry]
name = "my-fastapi-app"
version = "0.1.0"
description = "A modern FastAPI application with authentication"
authors = ["Your Name <you@example.com>"]
maintainers = ["Team <team@example.com>"]
license = "MIT"
readme = "README.md"
homepage = "https://example.com"
repository = "https://github.com/username/my-fastapi-app"
documentation = "https://docs.example.com"
keywords = ["fastapi", "api", "rest"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.11",
]Dependency Specifications
[tool.poetry.dependencies]
python = "^3.11" # Python version constraint
# Simple version constraint
fastapi = "^0.109.0" # >=0.109.0, <0.110.0
# Exact version
pydantic = "2.5.0" # Exactly 2.5.0
# Version range
sqlalchemy = ">=2.0.0,<3.0.0"
# With extras
uvicorn = {extras = ["standard"], version = "^0.27.0"}
# Multiple extras
databases = {extras = ["postgresql", "asyncpg"], version = "^0.8.0"}
# Optional dependencies
redis = {version = "^5.0.0", optional = true}
# Git dependencies
my-lib = {git = "https://github.com/username/my-lib.git", branch = "main"}
# Local path dependencies (for development)
my-local-lib = {path = "../my-local-lib", develop = true}
# Platform-specific dependencies
pywin32 = {version = "^305", platform = "win32"}Dependency Groups
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
black = "^23.12.1"
ruff = "^0.1.11"
[tool.poetry.group.test.dependencies]
pytest = "^7.4.3"
pytest-cov = "^4.1.0"
pytest-asyncio = "^0.23.3"
httpx = "^0.26.0"
[tool.poetry.group.docs.dependencies]
mkdocs = "^1.5.3"
mkdocs-material = "^9.5.3"Scripts and Commands
[tool.poetry.scripts]
start = "my_fastapi_app.main:start"
migrate = "my_fastapi_app.cli:migrate"
# Now you can run:
# poetry run start
# poetry run migrateWorking with Poetry
Common Commands
# Install all dependencies (including dev)
poetry install
# Install only production dependencies
poetry install --only main
# Add a new dependency
poetry add requests
# Add a development dependency
poetry add --group dev pytest
# Remove a dependency
poetry remove requests
# Update dependencies
poetry update
# Update a specific package
poetry update fastapi
# Show installed packages
poetry show
# Show package details
poetry show fastapi
# Show dependency tree
poetry show --tree
# Check for dependency issues
poetry check
# Build the project
poetry build
# Publish to PyPI
poetry publishRunning Commands
# Run Python scripts
poetry run python main.py
# Run FastAPI server
poetry run uvicorn main:app --reload
# Run tests
poetry run pytest
# Run formatters
poetry run black .
poetry run ruff check .
# Activate virtual environment
poetry shell
# Get virtual environment path
poetry env info --pathComplete FastAPI Project Structure with Poetry
my-fastapi-app/
├── pyproject.toml # Poetry configuration
├── poetry.lock # Locked dependencies
├── README.md
├── .gitignore
├── my_fastapi_app/
│ ├── __init__.py
│ ├── main.py
│ ├── config.py
│ ├── api/
│ │ ├── __init__.py
│ │ ├── routes/
│ │ │ ├── __init__.py
│ │ │ ├── users.py
│ │ │ └── items.py
│ │ └── dependencies.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── item.py
│ ├── schemas/
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── item.py
│ └── database.py
└── tests/
├── __init__.py
├── conftest.py
├── test_users.py
└── test_items.pyComplete pyproject.toml Example
[tool.poetry]
name = "my-fastapi-app"
version = "0.1.0"
description = "A production-ready FastAPI application"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
packages = [{include = "my_fastapi_app"}]
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.109.0"
uvicorn = {extras = ["standard"], version = "^0.27.0"}
sqlalchemy = "^2.0.25"
asyncpg = "^0.29.0"
pydantic-settings = "^2.1.0"
python-jose = {extras = ["cryptography"], version = "^3.3.0"}
passlib = {extras = ["bcrypt"], version = "^1.7.4"}
python-multipart = "^0.0.6"
alembic = "^1.13.1"
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
pytest-asyncio = "^0.23.3"
pytest-cov = "^4.1.0"
httpx = "^0.26.0"
black = "^23.12.1"
ruff = "^0.1.11"
mypy = "^1.8.0"
[tool.poetry.scripts]
dev = "uvicorn my_fastapi_app.main:app --reload"
migrate = "alembic upgrade head"
[tool.black]
line-length = 88
target-version = ['py311']
include = '\.pyi?$'
[tool.ruff]
line-length = 88
select = ["E", "F", "I"]
ignore = []
fixable = ["ALL"]
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
addopts = "--cov=my_fastapi_app --cov-report=term-missing"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"Environment Configuration
Using pydantic-settings
# config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "FastAPI App"
debug: bool = False
database_url: str
secret_key: str
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8"
)
settings = Settings().env File
# .env
APP_NAME="My FastAPI App"
DEBUG=true
DATABASE_URL=postgresql+asyncpg://user:pass@localhost/dbname
SECRET_KEY=your-secret-key-hereAdd python-dotenv
poetry add python-dotenvDocker Integration with Poetry
Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Install Poetry
RUN pip install poetry
# Copy dependency files
COPY pyproject.toml poetry.lock ./
# Install dependencies
RUN poetry config virtualenvs.create false \
&& poetry install --only main --no-interaction --no-ansi
# Copy application
COPY . .
# Run application
CMD ["uvicorn", "my_fastapi_app.main:app", "--host", "0.0.0.0", "--port", "8000"]Multi-stage Docker Build (Optimized)
# Stage 1: Build dependencies
FROM python:3.11-slim as builder
WORKDIR /app
RUN pip install poetry
COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
# Stage 2: Runtime
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "my_fastapi_app.main:app", "--host", "0.0.0.0", "--port", "8000"]Best Practices
1. Version Constraints
[tool.poetry.dependencies]
# Good: Allows minor updates
fastapi = "^0.109.0" # >=0.109.0, <0.110.0
# Good: Allows patch updates
pydantic = "~2.5.0" # >=2.5.0, <2.6.0
# Avoid: Too restrictive
sqlalchemy = "2.0.25" # Exactly this version
# Avoid: Too permissive
requests = "*" # Any version2. Use Dependency Groups
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
black = "^23.12.1"
[tool.poetry.group.test.dependencies]
pytest-cov = "^4.1.0"
httpx = "^0.26.0"
[tool.poetry.group.docs.dependencies]
mkdocs = "^1.5.3"3. Lock File in Version Control
# Always commit poetry.lock
git add poetry.lock
git commit -m "Update dependencies"4. Use Poetry Scripts
[tool.poetry.scripts]
dev = "uvicorn my_fastapi_app.main:app --reload --port 8000"
test = "pytest tests/ -v"
lint = "ruff check ."
format = "black ."5. Configure Tools in pyproject.toml
[tool.black]
line-length = 88
target-version = ['py311']
[tool.ruff]
line-length = 88
select = ["E", "F", "I", "N"]
[tool.mypy]
python_version = "3.11"
strict = true
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]Troubleshooting Common Issues
Issue: Poetry Install is Slow
# Use parallel installation
poetry config installer.parallel true
# Use modern installer
poetry config installer.modern-installation trueIssue: Dependency Conflicts
# Show dependency tree
poetry show --tree
# Update lock file
poetry lock --no-update
# Force update
poetry updateIssue: Virtual Environment Not Found
# Show environment info
poetry env info
# List all environments
poetry env list
# Remove environment
poetry env remove python3.11
# Create new environment
poetry installIssue: Cache Problems
# Clear cache
poetry cache clear pypi --all
# Reinstall dependencies
poetry install --no-cacheMigrating from requirements.txt
Export to requirements.txt
# Export all dependencies
poetry export -f requirements.txt --output requirements.txt
# Export without dev dependencies
poetry export -f requirements.txt --output requirements.txt --without dev
# Export without hashes (for compatibility)
poetry export -f requirements.txt --output requirements.txt --without-hashesImport from requirements.txt
# Add all requirements
cat requirements.txt | xargs poetry add
# Or manually
poetry add $(cat requirements.txt)Conclusion
Poetry and TOML have modernized Python project management:
- TOML provides a clean, readable configuration format
- Poetry handles dependency resolution automatically
- pyproject.toml centralizes all project configuration
- poetry.lock ensures reproducible builds
- Dependency groups separate concerns effectively
For FastAPI projects, Poetry offers:
- Easy dependency management
- Clear separation of dev/test/prod dependencies
- Automatic virtual environment handling
- Better collaboration through lock files
- Simplified deployment with Docker
Start using Poetry in your next FastAPI project and experience modern Python development!
Quick Reference
# Create project
poetry new my-app
poetry init
# Dependencies
poetry add fastapi
poetry add --group dev pytest
poetry remove requests
# Install & Run
poetry install
poetry run uvicorn main:app --reload
poetry shell
# Update & Maintain
poetry update
poetry show --tree
poetry check
# Export
poetry export -f requirements.txt --output requirements.txtPoetry makes managing FastAPI projects a breeze. Give it a try! 🚀
📬 Subscribe to Newsletter
Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.
We respect your privacy. Unsubscribe at any time.
💬 Comments
Sign in to leave a comment
We'll never post without your permission.