Week 1: Python Foundations & Environment Setup

This course assumes you can already write basic functions and use lists and dictionaries in some language — but not much more Python-specific than that. This week refreshes the modern Python features FastAPI code leans on constantly (type hints, dataclasses, structural pattern matching), gets a proper virtual environment and Poetry installed, and ends with your first FastAPI application running locally.

Module 1 of 12 Week 1 of 15 ~3–4 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Read and write modern Python: type hints, dataclasses and the match statement
  • Set up Python 3.12 with an isolated virtual environment managed by Poetry
  • Generate, run and understand your first FastAPI application

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.

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

models.py
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:

payments.py
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}"
Why this matters for FastAPI

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:

terminal
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:

terminal
# 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__)"
pyproject.toml — the essentials
[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"
No Poetry? Use venv + pip

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:

project layout
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.

src/app/main.py
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:

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

Try it now

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

Hands-on

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:

  1. Create a project with Poetry (or venv + pip) using the src layout shown above, with FastAPI and Uvicorn installed.
  2. Confirm it runs with uvicorn app.main:app --reload --app-dir src and that GET /hello responds over curl or /docs.
  3. Define a @dataclass Greeting with fields message: str and word_count: int.
  4. Add a GET /greet endpoint with an optional name: str = "world" query parameter, returning a Greeting (FastAPI will serialize a dataclass to JSON automatically).
  5. Model Ok(greeting: Greeting) and Invalid(reason: str) as dataclasses, and use a match statement to turn either result into the right response — return Invalid when name is blank or over 50 characters.
Hint

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?

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?

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?

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?

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.