1. Modern Python Essentials
FastAPI's entire design — request validation, auto-generated docs, editor autocomplete — is built on Python's type hints. Alongside them, this course leans on dataclasses and the structural pattern matching introduced in Python 3.10, both of which show up constantly once request/response models appear from Week 3 onward.
Type hints
A type hint doesn't change how Python runs your code — it's not enforced at
runtime by the interpreter itself. What it does is let tools (your editor,
mypy, and critically, FastAPI itself) understand your code's shapes
well enough to validate data and generate documentation automatically.
def build_greeting(name: str, excited: bool = False) -> str:
punctuation = "!" if excited else "."
return f"Hello, {name}{punctuation}"
# Editors and FastAPI both read these hints:
# name must be a str, excited defaults to False, return value is a str
Dataclasses
A @dataclass generates __init__, __repr__ and
__eq__ from a one-line field list — the same boilerplate-elimination
idea as a Java record, though (unlike Pydantic models, which you'll meet in Week 2)
it does no runtime validation on its own.
from dataclasses import dataclass
@dataclass
class Customer:
id: int
name: str
email: str
c = Customer(1, "Ada Lovelace", "ada@example.com")
c.name # "Ada Lovelace" -- generated attribute access
c == Customer(1, "Ada Lovelace", "ada@example.com") # True -- field-by-field equality
print(c) # Customer(id=1, name='Ada Lovelace', email='ada@example.com')
Structural pattern matching
Python's match statement (3.10+) can destructure a value's shape
directly, replacing a chain of isinstance checks with one readable
block:
from dataclasses import dataclass
@dataclass
class Success:
transaction_id: str
@dataclass
class Declined:
reason: str
def describe(result: Success | Declined) -> str:
match result:
case Success(transaction_id=tx_id):
return f"Payment succeeded: {tx_id}"
case Declined(reason=reason):
return f"Payment declined: {reason}"
Type hints aren't optional decoration in this course — FastAPI reads them at import time to validate incoming requests, serialize responses, and generate the interactive docs you'll use starting Week 2. Getting comfortable with them now pays off every week after.
2. Python 3.12 & Poetry
This course uses Python 3.12. Install it, then confirm it's active:
python3 --version
# Python 3.12.x
Every Python project should run inside an isolated virtual environment — its own private copy of installed packages, separate from your system Python, so one project's dependencies never collide with another's. This course uses Poetry, which manages both the virtual environment and a lockfile of exact dependency versions in one tool:
# Install Poetry (one-time, per machine)
curl -sSL https://install.python-poetry.org | python3 -
# Create a new project
poetry new week-01 --name app
cd week-01
# Add dependencies -- this updates pyproject.toml and installs into a venv
poetry add fastapi "uvicorn[standard]"
# Run any command inside that virtual environment
poetry run python -c "import fastapi; print(fastapi.__version__)"
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Ada Lovelace"]
[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.115"
uvicorn = {extras = ["standard"], version = "^0.32"}
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Everything in this course also works with the standard library's python3 -m venv .venv, source .venv/bin/activate, and pip install fastapi "uvicorn[standard]" — Poetry just adds a lockfile and a cleaner dependency-declaration format on top of the same underlying idea.
3. Project Structure & Dependency Management
This course uses the src layout — application code lives under
src/, kept separate from tests and config, which avoids a whole class of
import bugs where Python accidentally imports your uninstalled source directory
instead of the installed package:
week-01/
├── pyproject.toml
├── poetry.lock
├── src/
│ └── app/
│ ├── __init__.py
│ └── main.py ← entry point
└── tests/
└── test_main.py
src/app/main.py is where your FastAPI instance and routes will live;
tests/ mirrors it for pytest, which you'll set up properly in Week 8.
This convention-over-configuration layout is one reason well-structured FastAPI
projects look similar across companies.
4. Your First FastAPI Application
A FastAPI app starts from one FastAPI() instance — creating it is
"the switch that turns this module into a runnable web application." You'll unpack
routing and validation properly in Weeks 2–3; for now, treat it as the minimum
needed to prove the whole chain works end to end.
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def hello() -> dict[str, str]:
return {"message": "Hello from FastAPI on Python 3.12!"}
@app.get("/hello") registers a route: an HTTP GET request
to /hello calls hello(), and FastAPI serializes whatever
it returns to JSON automatically — no manual json.dumps call needed.
You'll build real routers and Pydantic response models properly starting Week 2;
this one exists just to prove the setup works.
5. Running & Auto-Reload
FastAPI apps run behind Uvicorn, an ASGI server. During development,
its --reload flag restarts the app automatically whenever you save a
file — the fastest loop while you're actively coding:
poetry run uvicorn app.main:app --reload --app-dir src
# Then, in another terminal:
curl http://localhost:8000/hello
# {"message":"Hello from FastAPI on Python 3.12!"}
app.main:app means "import the app module inside the
app package, and use the object named app inside it" —
exactly the FastAPI() instance you created above. FastAPI also generates
interactive API documentation for free at /docs, which you'll use
heavily once real request bodies show up in Week 3.
With the server running, open http://localhost:8000/docs in a browser — that live, interactive documentation page is generated entirely from your route's type hints, and you'll be relying on it constantly through the rest of this course.
6. Hands-on Exercise
Generate, run and extend your first FastAPI service
Get the full local loop working, then apply this week's modern-Python features to a small typed response.
Requirements:
- Create a project with Poetry (or
venv+pip) using thesrclayout shown above, with FastAPI and Uvicorn installed. - Confirm it runs with
uvicorn app.main:app --reload --app-dir srcand thatGET /helloresponds overcurlor/docs. - Define a
@dataclass Greetingwith fieldsmessage: strandword_count: int. - Add a
GET /greetendpoint with an optionalname: str = "world"query parameter, returning aGreeting(FastAPI will serialize a dataclass to JSON automatically). - Model
Ok(greeting: Greeting)andInvalid(reason: str)as dataclasses, and use amatchstatement to turn either result into the right response — returnInvalidwhennameis blank or over 50 characters.
You don't need a proper JSON error-response shape yet — that's Week 3's exception-handler topic. Returning a plain string for the Invalid case with status_code=400 via a JSONResponse is enough for this exercise.
7. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
Does writing def build_greeting(name: str) -> str: stop you from calling it with an integer at runtime?
Does writing def build_greeting(name: str) -> str: stop you from calling it with an integer at runtime?
No — plain Python type hints are not enforced by the interpreter itself; calling build_greeting(42) would run without error unless something inside the function fails. Hints exist for tools like editors, mypy, and FastAPI to read and act on. FastAPI is the exception in practice: it uses your hints to actively validate incoming request data and will reject a bad request before your function body ever runs.
Q2
What does @dataclass generate for you that you'd otherwise write by hand?
What does @dataclass generate for you that you'd otherwise write by hand?
An __init__ method accepting each declared field, a __repr__ for readable printing, and an __eq__ that compares instances field by field — all generated from the class's type-annotated field list. Unlike a Pydantic model (introduced in Week 2), it performs no runtime validation of the values passed in.
Q3
Why does this course put application code under src/app/ instead of directly in the project root?
Why does this course put application code under src/app/ instead of directly in the project root?
The "src layout" prevents Python from silently importing your local, uninstalled source directory instead of the properly installed package — a common bug where tests pass locally by accident but fail once the package is actually installed elsewhere, because a stray root-level module shadowed the real one.
Q4
In uvicorn app.main:app --reload, what does app.main:app refer to?
In uvicorn app.main:app --reload, what does app.main:app refer to?
It tells Uvicorn to import the main module from the app package, then use the object named app defined inside it — the FastAPI() instance created with app = FastAPI(). Uvicorn is the ASGI server that actually accepts HTTP connections and hands each request to that FastAPI instance; --reload just restarts this process automatically whenever a source file changes.