← Teacher guide

The same API in FastAPI

An optional lesson. Students rebuild the club API in FastAPI, see typed inputs checked and converted for them (with 422 replies for bad input), then add a checked optional input, a leader route and an API of their own.

Web apps with Flask · APIs · about 45 minutes · Open the student lesson

Classroom resources

Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.

Students will be able to

Timing

Predict and Run 10 min · Investigate 10 min · Modify 15 min · Make 10 min

Before class

Optional: not needed for completing the course. Read the unit primer first. FastAPI routes are @api.get("/address"); parameters in the address use curly brackets (/members/{group}), and other parameters come from the query string (?name=ad), optional when they have a default. The type after each parameter (age: int) makes FastAPI convert the value and reply 422 with an explanation when it cannot, before the function runs (Pydantic does the checking). Without a type, the value is text (the bug task: "12" + 1 is an error, 500). uvicorn.run(api) starts the app; in the playground it shows in the App tab, on a computer pip install fastapi uvicorn and python app.py serve it at http://127.0.0.1:8000, with an automatic documentation page at /docs (not available in the playground). No async, dependency injection or Pydantic models in this lesson.

Running the lesson

  1. Predict: compare with Flask, where you would write int() and try/except yourself.
  2. Run: try /members?name=ad, then /members/Players, then /members/players.
  3. Modify: send min_wins=lots and read the 422 reply together.
  4. Info: list what each tool is good for on the board.

The hard part (on purpose)

Reading the 422 reply. Have students read the error JSON aloud and find the field it names before changing anything.

When students get stuck

What you will seeA question to askThe fix
422 with "Field required".Does the input have a default, or is it in the address?Add a default (min_wins: int = 0), or send it: /results?min_wins=3.
500 Internal Server Error on a number input.Does the parameter have a type?age: int (or float).
404 for /members/Players.Does the route use curly brackets for the part that changes?@api.get("/members/{group}").

Answers to the checks

  1. Predict: Someone visits /next-year?age=twelve. What do they get?
    A 422 error reply. age: int tells FastAPI to check and convert age; "twelve" is not a whole number, so FastAPI replies with what is wrong, and next_year never runs.
  2. From the query string: Where does name come from in members(name: str = "")?
    The query string, such as /members?name=ad; it is "" if there is none
    Parameters that are not in the address pattern are read from the query string. The default makes it optional.
  3. What FastAPI did for you: Compared with Flask's request.args.get("name", ""), what did FastAPI save you?
    Reading the query string yourself: the parameter is filled in from it
    FastAPI matches parameters with the address and query string by name, and converts them to their types. The rest of the code is the same Python.
  4. Spot the bug: /next-year?age=12 gives 500 Internal Server Error instead of {"next": 13}. Click the line that is wrong.
    line 8
    Without a type, age is the text "12", and "12" + 1 is an error. age: int makes FastAPI convert it (and reject anything that is not a whole number).
  5. Where this leads: Which would you choose for a website with pages and forms, and which for an API that apps use?
    Flask for pages and forms; FastAPI for an API (both can do either)
    Flask is simple and good at pages and templates. FastAPI shines at APIs: checked inputs and automatic documentation. Both are real tools used every day.

Solutions

One possible solution for each task. Other correct programs are fine.

Modify › At least some wins

import csv
from fastapi import FastAPI
import uvicorn

api = FastAPI()


def load_results():
    with open("results.csv") as f:
        return list(csv.DictReader(f))


@api.get("/results")
def results(min_wins: int = 0):
    rows = []
    for r in load_results():
        if int(r["wins"]) >= min_wins:
            rows.append({"player": r["player"], "wins": int(r["wins"])})
    return {"results": rows}


uvicorn.run(api)

Modify › The leader

import csv
from fastapi import FastAPI
import uvicorn

api = FastAPI()


def load_results():
    with open("results.csv") as f:
        return list(csv.DictReader(f))


@api.get("/results")
def results():
    rows = []
    for r in load_results():
        rows.append({"player": r["player"], "wins": int(r["wins"])})
    return {"results": rows}



@api.get("/leader")
def leader():
    rows = load_results()
    best = rows[0]
    for r in rows:
        if int(r["wins"]) > int(best["wins"]):
            best = r
    return {"player": best["player"], "wins": int(best["wins"])}


uvicorn.run(api)

an API of your own

from fastapi import FastAPI
import uvicorn

api = FastAPI()


@api.get("/seconds")
def seconds(minutes: float):
    return {"minutes": minutes, "seconds": round(minutes * 60)}


@api.get("/tables")
def tables(n: int, up_to: int = 10):
    rows = []
    for i in range(1, up_to + 1):
        rows.append(str(i) + " x " + str(n) + " = " + str(i * n))
    return {"table": rows}


uvicorn.run(api)

Stretch and support

Stretch: Return an error of your own for a sensible-looking but wrong value (from fastapi import HTTPException; raise HTTPException(404, "No such player")).

Support: Give students the min_wins parameter line, and ask them only to add the if in the loop.

Exit ticket

What does age: int do in a FastAPI route?

FastAPI converts the input to a whole number, and replies with a 422 error explaining the problem if it cannot, before the function runs.

What each step is for

Where it leads

The end of the course. Next steps: SQLite for storage (sqlite3 is built into Python), hosting a Flask or FastAPI app (with a teacher), or the planned Machine learning with Gradio course.