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.
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.
Predict and Run 10 min · Investigate 10 min · Modify 15 min · Make 10 min
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.
Reading the 422 reply. Have students read the error JSON aloud and find the field it names before changing anything.
| What you will see | A question to ask | The 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}"). |
One possible solution for each task. Other correct programs are fine.
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)
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)
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: 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.
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.
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.