Build the club API again in FastAPI, where each input's type is checked and converted for you, and wrong input gets a clear error reply.
FastAPI is a newer Python library for APIs, used by companies big and small. Its trick: the types you write are checked for you.
In Flask, a number in the address arrives as text, and you check and convert it yourself. FastAPI reads the type you give each input (age: int) and does it for you: it turns "12" into 12, and replies with an error explaining the problem when someone sends twelve. Routes are written @api.get("/address"), and uvicorn.run(api) starts the app (here, in the App tab).
Make your prediction first. Help opens once you've locked it in.
from fastapi import FastAPI
import uvicorn
api = FastAPI()
@api.get("/next-year")
def next_year(age: int):
return {"next": age + 1}
uvicorn.run(api)
age: int says what type age must be. What happens to a value that is not a whole number?
Run it (FastAPI takes a moment to load the first time) and try the addresses on the home page. In members(name: str = ""), a parameter that is not in the route's address comes from the query string, and = "" makes it optional. In /members/{group}, curly brackets mark the part that changes (Flask uses angle brackets).
import csv
from fastapi import FastAPI
import uvicorn
api = FastAPI()
def load_members():
with open("members.csv") as f:
return list(csv.DictReader(f))
@api.get("/")
def home():
return {"message": "Chess club API. Try /members, /members?name=ad and /members/Players"}
@api.get("/members")
def members(name: str = ""):
found = []
for m in load_members():
if name.lower() in m["name"].lower():
found.append(m)
return {"members": found}
@api.get("/members/{group}")
def group_members(group: str):
found = []
for m in load_members():
if m["group"] == group:
found.append(m)
return {"group": group, "count": len(found), "members": found}
uvicorn.run(api)
import csv
from fastapi import FastAPI
import uvicorn
api = FastAPI()
def load_members():
with open("members.csv") as f:
return list(csv.DictReader(f))
@api.get("/")
def home():
return {"message": "Chess club API. Try /members, /members?name=ad and /members/Players"}
@api.get("/members")
def members(name: str = ""):
found = []
for m in load_members():
if name.lower() in m["name"].lower():
found.append(m)
return {"members": found}
@api.get("/members/{group}")
def group_members(group: str):
found = []
for m in load_members():
if m["group"] == group:
found.append(m)
return {"group": group, "count": len(found), "members": found}
uvicorn.run(api)
Where an input comes from.
Compared with Flask.
A number that arrived as text.
/next-year?age=12 gives 500 Internal Server Error instead of {"next": 13}. Click the line that is wrong.
Add an optional min_wins input that FastAPI checks as a whole number. Try /results?min_wins=lots too.
def results(min_wins: int = 0):, and only add rows whose wins are at least min_wins. Try /results?min_wins=3, then /results?min_wins=lots.
Add /leader, which sends the player with the most wins.
@api.get("/leader"), with the biggest-so-far loop from Data detective or Lesson 5.
Let /results?min_wins=3 send only players with at least 3 wins (checked as a whole number).
Run your program. This ticks when your code does all of the above.
Add /leader, which sends the player with the most wins.
Run your program. This ticks when your code does all of the above.
Make a small API with at least one int or float input: minutes to seconds, a times table, a dice roller with a number of sides. Test it with a good value and a wrong one.
Make an API of your own with at least one int or float input, and test it with a wrong value.
Run your program. This ticks when your code does all of the above.
On a computer, FastAPI also makes a documentation page at /docs from your types, where you can try every route (it needs a computer: the playground cannot show it). The checks come from a library called Pydantic, which can also check whole JSON bodies sent to an API.
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. About 45 minutes.
Open the teacher guide: answers, solutions, slides and a worksheet.
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.
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.
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.