Students turn the club's data into an API, with routes that return dictionaries sent as JSON, then add a stats route, a search with a query string and an API of their own for the results.
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
Read the unit primer first. A Flask route that returns a dictionary sends it as JSON (Flask sorts the keys); the App tab shows it neatly. An API is a set of addresses that send data for programs, not pages for people. The lesson reads members.csv from Lesson 4 (the Files tab can put the original back). The bug task returns the function load_members instead of calling it, giving 500 Internal Server Error (the console shows the error: a function cannot be sent as JSON). The query string (?name=ad) is read with request.args.get("name", ""), where the second value is used when it is missing. The info question raises privacy: an API shares data with anyone who asks.
Seeing JSON as data for programs, not a page. Ask how another program would use the reply, before adding routes.
| What you will see | A question to ask | The fix |
|---|---|---|
| 500 Internal Server Error and "not JSON serializable" in the console. | Is something in the dictionary a function, or not a number, text, list or dictionary? | Call the function: load_members(). |
| The search finds nobody. | Are both sides lower case? | name in m["name"].lower(), with name lowered too. |
| Wins come out as "5" in quotes. | Is wins text or a number in the dictionary? | int(r["wins"]). |
One possible solution for each task. Other correct programs are fine.
import csv
from flask import Flask, request
app = Flask(__name__)
def load_members():
with open("members.csv") as f:
return list(csv.DictReader(f))
@app.route("/")
def home():
page = "<h1>Chess club API</h1>"
page = page + "<p><a href='/api/members'>/api/members</a></p>"
page = page + "<p><a href='/api/members/Players'>/api/members/Players</a></p>"
return page
@app.route("/api/members")
def api_members():
return {"members": load_members()}
@app.route("/api/members/<group>")
def api_group(group):
found = []
for m in load_members():
if m["group"] == group:
found.append(m)
return {"group": group, "count": len(found), "members": found}
@app.route("/api/stats")
def api_stats():
counts = {}
for m in load_members():
if m["group"] in counts:
counts[m["group"]] = counts[m["group"]] + 1
else:
counts[m["group"]] = 1
return counts
app.run()
import csv
from flask import Flask, request
app = Flask(__name__)
def load_members():
with open("members.csv") as f:
return list(csv.DictReader(f))
@app.route("/")
def home():
page = "<h1>Chess club API</h1>"
page = page + "<p><a href='/api/members'>/api/members</a></p>"
page = page + "<p><a href='/api/members/Players'>/api/members/Players</a></p>"
return page
@app.route("/api/members")
def api_members():
name = request.args.get("name", "").lower()
found = []
for m in load_members():
if name in m["name"].lower():
found.append(m)
return {"members": found}
@app.route("/api/members/<group>")
def api_group(group):
found = []
for m in load_members():
if m["group"] == group:
found.append(m)
return {"group": group, "count": len(found), "members": found}
app.run()
import csv
from flask import Flask
app = Flask(__name__)
def load_results():
with open("results.csv") as f:
return list(csv.DictReader(f))
@app.route("/api/results")
def api_results():
rows = []
for r in load_results():
rows.append({"player": r["player"], "wins": int(r["wins"])})
return {"results": rows}
@app.route("/api/leader")
def api_leader():
rows = load_results()
leader = rows[0]
for r in rows:
if int(r["wins"]) > int(leader["wins"]):
leader = r
return {"player": leader["player"], "wins": int(leader["wins"])}
app.run()
Stretch: Add /api/members/<group>/count that sends only a number, and explain why a program might want that.
Support: Give students the stats loop, and ask them only to write the route and the return.
How is an API route different from a page route?
It sends data (JSON) for a program to use, instead of an HTML page for a person to read.
The optional FastAPI lesson builds the same API with type checks done for you. Data detective's optional API lesson calls real APIs such as Wikidata.