Back to blog

Managing FastAPI Projects with Poetry and TOML

pythonfastapipoetrytomldependencies
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.0

TOML 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 sqlalchemy

2. 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.lock

4. Virtual Environment Management

Poetry creates and manages virtual environments automatically:

# Poetry handles everything
poetry install
poetry run uvicorn main:app --reload

Setting 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 --version

Creating 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 init

The 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 mypy

Your 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 migrate

Working 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 publish

Running 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 --path

Complete 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.py

Complete 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-here

Add python-dotenv

poetry add python-dotenv

Docker 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 version

2. 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 true

Issue: Dependency Conflicts

# Show dependency tree
poetry show --tree
 
# Update lock file
poetry lock --no-update
 
# Force update
poetry update

Issue: 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 install

Issue: Cache Problems

# Clear cache
poetry cache clear pypi --all
 
# Reinstall dependencies
poetry install --no-cache

Migrating 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-hashes

Import 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.txt

Poetry 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.