Getting a FastAPI application running takes under five minutes โ install two packages, write five lines of Python, and start the server. The simplicity of the first hello world is intentional: FastAPI front-loads all the complexity into type annotations and Pydantic models rather than framework-specific configuration files. This lesson walks through installation, the minimal app, the recommended project structure for a real application, and the development workflow that makes building FastAPI applications efficient.
Installation
# Create a virtual environment (recommended)
python3 -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
# Install FastAPI and Uvicorn
pip install fastapi uvicorn[standard]
# uvicorn[standard] includes:
# - websockets (for WebSocket support)
# - httptools (faster HTTP parsing)
# - watchfiles (for --reload in development)
# - python-dotenv (for .env files)
# Verify
python -c "import fastapi; print(fastapi.__version__)"
python -c "import uvicorn; print(uvicorn.__version__)"
uvicorn[standard] installs Uvicorn with all optional C-accelerated dependencies. The plain pip install uvicorn installs a pure-Python version that works but is slower. For production, always use the standard install. Uvicorn is the ASGI server โ FastAPI defines your application, Uvicorn handles the actual TCP connections, HTTP parsing, and passes requests to FastAPI’s ASGI interface.uvicorn main:app --reload during development โ the --reload flag watches your Python files for changes and automatically restarts the server. This means you save a file and the change is reflected immediately without manually restarting. Do NOT use --reload in production โ it adds overhead and is designed only for development.--workers 4), each worker is a separate process with its own memory space. Any in-memory state (global variables, dicts) is NOT shared between workers. If you store data in a global dict, each worker has its own copy. Use a database or Redis for any state that must be shared across workers or requests.The Minimal Application
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "Hello, World!"}
@app.get("/health")
def health_check():
return {"status": "ok"}
# Run the application
uvicorn main:app --reload
# Output:
# INFO: Will watch for changes in these directories: ['/path/to/project']
# INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
# INFO: Started reloader process [12345]
# Test it
curl http://localhost:8000/
# {"message":"Hello, World!"}
# Visit docs
# http://localhost:8000/docs โ Swagger UI
# http://localhost:8000/redoc โ ReDoc
Recommended Project Structure
blog-api/
โโโ app/
โ โโโ __init__.py
โ โโโ main.py # FastAPI app instance, router includes, lifespan
โ โโโ config.py # pydantic-settings Settings class
โ โโโ database.py # SQLAlchemy engine, SessionLocal, Base
โ โ
โ โโโ models/ # SQLAlchemy ORM models (one file per resource)
โ โ โโโ __init__.py
โ โ โโโ user.py
โ โ โโโ post.py
โ โ
โ โโโ schemas/ # Pydantic request/response models
โ โ โโโ __init__.py
โ โ โโโ user.py # UserCreate, UserUpdate, UserResponse
โ โ โโโ post.py # PostCreate, PostUpdate, PostResponse
โ โ
โ โโโ routers/ # Route handlers grouped by resource
โ โ โโโ __init__.py
โ โ โโโ users.py # /users endpoints
โ โ โโโ posts.py # /posts endpoints
โ โ
โ โโโ dependencies.py # Shared FastAPI dependencies (get_db, get_current_user)
โ
โโโ alembic/ # Database migrations
โ โโโ env.py
โ โโโ versions/
โ
โโโ tests/
โ โโโ conftest.py # pytest fixtures
โ โโโ test_posts.py
โ
โโโ alembic.ini
โโโ requirements.txt
โโโ .env
Application Factory Pattern
# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.routers import posts, users, auth
from app.config import settings
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: connect to DB pool, warm caches, etc.
print("Application starting...")
yield
# Shutdown: close DB connections, flush queues
print("Application shutting down...")
def create_app() -> FastAPI:
app = FastAPI(
title = settings.app_name,
version = "1.0.0",
lifespan = lifespan,
docs_url = "/docs" if settings.environment != "production" else None,
)
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins = settings.allowed_origins,
allow_credentials = True,
allow_methods = ["*"],
allow_headers = ["*"],
)
# Include routers
app.include_router(auth.router, prefix="/auth", tags=["Auth"])
app.include_router(users.router, prefix="/users", tags=["Users"])
app.include_router(posts.router, prefix="/posts", tags=["Posts"])
return app
app = create_app()
# Run: uvicorn app.main:app --reload
Common Mistakes
Mistake 1 โ Defining routes directly on app in large applications
โ Wrong โ all routes in main.py becomes unmanageable:
@app.get("/posts")
@app.post("/posts")
@app.get("/users")
# ... 50 more routes in main.py
โ
Correct โ use APIRouter in separate modules, include in main:
from fastapi import APIRouter
router = APIRouter()
@router.get("/")
def list_posts(): ...
# In main.py: app.include_router(router, prefix="/posts")
Mistake 2 โ Using –reload in production
โ Wrong:
uvicorn app.main:app --reload --workers 4 # --reload incompatible with --workers!
โ Correct production command:
uvicorn app.main:app --workers 4 --host 0.0.0.0 --port 8000
Mistake 3 โ Not using a virtual environment
โ Wrong โ installing globally creates version conflicts across projects.
โ
Correct โ always use python3 -m venv venv per project.
Quick Reference
| Task | Command / Code |
|---|---|
| Install | pip install fastapi uvicorn[standard] |
| Run dev | uvicorn main:app --reload |
| Run prod | uvicorn app.main:app --workers 4 --host 0.0.0.0 |
| Create app | app = FastAPI(title="...", version="...") |
| Include router | app.include_router(router, prefix="/posts") |
| Swagger docs | http://localhost:8000/docs |
| OpenAPI schema | http://localhost:8000/openapi.json |