After this lesson, you will be able to: Build a typed, async Python backend with FastAPI: validate requests with Pydantic, talk to PostgreSQL through SQLAlchemy, manage schema changes with Alembic, and run it in Docker.
Python's dominant modern web framework is FastAPI: async, type-driven, and fast. It uses the type hints you already learned to validate input, serialize output, and generate interactive API docs for free. This lesson is the backend stack employers actually ask for, FastAPI + Pydantic + SQLAlchemy + Alembic, and it turns your Python knowledge into a deployable service.
Flask is minimal but unopinionated and sync-first; Django is batteries-included but heavy and built around server-rendered pages. FastAPI hits the modern sweet spot for APIs: it is async-native (great for I/O-bound workloads), it uses Python type hints to validate and document automatically, and it is one of the fastest Python frameworks. The headline feature: declare a Pydantic model for your request body and FastAPI validates incoming JSON against it, returns a clear 422 on bad input, and publishes interactive Swagger docs at /docs with zero extra code. Django still wins when you want an admin panel and tight ORM integration; for a typed JSON API in 2025, FastAPI is the default.
The type hints are the validation. FastAPI reads them at runtime.
from fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModel, EmailStr, Fieldapp = FastAPI()# Pydantic model = request schema + validation + docs, all from typesclass CreateUser(BaseModel):email: EmailStrname: str = Field(min_length=1, max_length=100)age: int = Field(ge=0, le=150)class UserOut(BaseModel):id: intemail: EmailStrname: str@app.post("/users", response_model=UserOut, status_code=201)async def create_user(payload: CreateUser):# payload is already validated; bad JSON never reaches here (FastAPI returns 422)user = await db_create_user(payload) # your DB callreturn user@app.get("/users/{user_id}", response_model=UserOut)async def get_user(user_id: int):user = await db_get_user(user_id)if user is None:raise HTTPException(status_code=404, detail="User not found")return user# Run: uvicorn main:app --reload -> interactive docs at http://localhost:8000/docs
SQLAlchemy is the standard Python ORM; Alembic versions your schema the way Prisma Migrate does for Node.
# models.py: SQLAlchemy 2.0 typed modelsfrom sqlalchemy.orm import DeclarativeBase, Mapped, mapped_columnclass Base(DeclarativeBase): ...class User(Base):__tablename__ = "users"id: Mapped[int] = mapped_column(primary_key=True)email: Mapped[str] = mapped_column(unique=True)name: Mapped[str]# Alembic: version-controlled schema changes# alembic init alembic # one-time setup# alembic revision --autogenerate -m "create users" # diff models -> migration# alembic upgrade head # apply to the database# alembic downgrade -1 # roll back one revision# Never edit a shipped table by hand; generate a migration so every# environment applies the same change in the same order.
FastAPI runs under an ASGI server, usually uvicorn (or gunicorn with uvicorn workers in production). For deployment you containerize it: a slim Python base image, install dependencies, copy code, run uvicorn, and run as a non-root user (the Docker security lesson applies directly). Push the image and deploy to Railway or Render (both have free tiers and detect a Dockerfile automatically), or to any container host. Put secrets (the database URL) in environment variables, never in the image. The result is a live, documented API at a public URL, which is exactly the passion project this subtrack builds toward.
Defining sync endpoints that call blocking I/O on the async event loop, which stalls the whole server. Use async DB drivers (asyncpg via SQLAlchemy async) or run blocking work in a threadpool. Reusing a SQLAlchemy model as the API response and leaking columns (password hashes, internal flags). Return a Pydantic response_model. Editing the database by hand instead of generating an Alembic migration, so environments drift. Skipping `response_model`, losing both output validation and accurate docs. Hardcoding the database URL instead of reading it from an environment variable, then committing the credential.
Pick the most complete answer.
Sign in and purchase access to unlock this lesson.