Python’s virtual environment system solves a fundamental problem: different projects may require different versions of the same package, and installing everything globally leads to version conflicts. A virtual environment is an isolated Python installation โ it has its own copy of pip and its own site-packages directory. Every Python project you work on should have its own virtual environment. For FastAPI, this means a clean, reproducible environment where fastapi, sqlalchemy, pydantic, and all other dependencies are version-pinned and isolated from other projects on your machine.
Creating and Activating Virtual Environments
# Create a virtual environment in the 'venv' directory
python3 -m venv venv
# Activate โ changes your shell to use venv's Python and pip
# macOS / Linux:
source venv/bin/activate
# Windows:
venv\Scripts\activate
# Verify you're in the venv โ prompt changes to (venv)
which python # /path/to/project/venv/bin/python
pip --version # pip X.Y.Z from /path/to/project/venv/...
# Deactivate โ return to global Python
deactivate
# Tip: name the directory 'venv' or '.venv' โ both are conventional
# .venv is preferred for modern projects (hidden directory)
python3 -m venv .venv
source .venv/bin/activate # same process
venv/ and .venv/ to your .gitignore โ virtual environment directories are large (hundreds of MB) and machine-specific. They should never be committed to version control. Instead, commit your requirements.txt (or pyproject.toml) which lists the packages needed to recreate the environment. Any developer can run pip install -r requirements.txt to get an identical environment.requirements.txt for production dependencies (fastapi, sqlalchemy, pydantic, etc.) and requirements-dev.txt that starts with -r requirements.txt and adds development-only packages (pytest, httpx, black, ruff, mypy). This separation keeps production images lean while development environments have all tooling available.pip โ Installing and Managing Packages
# Install a package
pip install fastapi
pip install "fastapi[all]" # with optional extras (uvicorn, etc.)
pip install "sqlalchemy>=2.0,<3.0" # version constraint
# Install from requirements file
pip install -r requirements.txt
pip install -r requirements-dev.txt
# Upgrade a package
pip install --upgrade fastapi
# Uninstall
pip uninstall fastapi
# Show installed packages
pip list # all installed packages
pip freeze # name==version format (for requirements.txt)
pip show fastapi # info about a specific package
# Generate requirements.txt from current environment
pip freeze > requirements.txt
# Check for security vulnerabilities
pip audit # pip 23.0+ has built-in audit
requirements.txt for FastAPI Projects
# requirements.txt โ production dependencies
fastapi==0.111.0
uvicorn[standard]==0.29.0
sqlalchemy==2.0.30
alembic==1.13.1
pydantic==2.7.1
pydantic-settings==2.2.1
psycopg2-binary==2.9.9 # PostgreSQL driver
python-jose[cryptography]==3.3.0 # JWT
passlib[bcrypt]==1.7.4 # password hashing
python-multipart==0.0.9 # file uploads
httpx==0.27.0 # async HTTP client (for tests too)
python-dotenv==1.0.1 # .env file support
# requirements-dev.txt โ development + test dependencies
-r requirements.txt # include all production deps first
pytest==8.2.0
pytest-asyncio==0.23.7
black==24.4.2 # code formatter
ruff==0.4.4 # fast linter
mypy==1.10.0 # static type checker
Modern: pyproject.toml
# pyproject.toml โ modern all-in-one project configuration
# Replaces setup.py, setup.cfg, and partly requirements.txt
[project]
name = "blog-api"
version = "1.0.0"
description = "FastAPI blog application"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.111.0",
"uvicorn[standard]>=0.29.0",
"sqlalchemy>=2.0.0",
"alembic>=1.13.0",
"pydantic>=2.7.0",
"pydantic-settings>=2.2.0",
"psycopg2-binary>=2.9.9",
"python-jose[cryptography]>=3.3.0",
"passlib[bcrypt]>=1.7.4",
"python-dotenv>=1.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"black>=24.0.0",
"ruff>=0.4.0",
"mypy>=1.10.0",
]
[tool.black]
line-length = 88
[tool.ruff]
line-length = 88
select = ["E", "F", "I"]
[tool.mypy]
python_version = "3.11"
strict = true
Common Mistakes
Mistake 1 โ Installing packages globally instead of in venv
โ Wrong โ forgetting to activate the venv:
pip install fastapi # installs globally if venv not activated
โ Correct โ always activate first:
source venv/bin/activate
pip install fastapi # installs into venv โ
Mistake 2 โ Pinning to patch version when minor version constraint is enough
โ Wrong โ overly strict pinning causes update friction:
fastapi==0.111.0 # exact pin โ will miss security patches
โ Better โ pin to compatible version:
fastapi>=0.111.0,<0.112.0 # or use ~=0.111.0 (compatible release)
Mistake 3 โ Committing venv to git
โ Wrong โ venv directory in the repository:
git add venv/ # adds hundreds of MB of binary files to git
โ Correct โ add to .gitignore:
# .gitignore
venv/
.venv/
__pycache__/
*.pyc
.env
Quick Reference
| Task | Command |
|---|---|
| Create venv | python3 -m venv .venv |
| Activate (Mac/Linux) | source .venv/bin/activate |
| Activate (Windows) | .venv\Scripts\activate |
| Install package | pip install package-name |
| Install from file | pip install -r requirements.txt |
| Save requirements | pip freeze > requirements.txt |
| Deactivate | deactivate |