← Web apps with Flask

An API for the club

Send the club's data as JSON, so other programs (an app, a game, another website) can read it: routes that return data instead of pages.

Weather apps, maps and music players get their data from APIs: addresses that send back data for programs, not pages for people.

A page is for people to read. An API is for programs: it sends back data. In Flask, a route that returns a dictionary sends it as JSON, a text format almost every language can read. The App tab shows JSON neatly; another program would turn it straight back into a dictionary or list.

PredictRead the code and say what it will do before you run anything. A wrong guess is fine: it shows you what to look for.

Predict

api.py
from flask import Flask

app = Flask(__name__)


@app.route("/api/club")
def club():
    return {"name": "Chess club", "room": 12, "days": ["Tuesday", "Thursday"]}


app.run()

The function returns a dictionary, not HTML. What does Flask send?

Someone visits /api/club. What do they get?

RunRun it and compare the result with your prediction.

Run

Run it and follow the links, then try /api/members/Champions and /api/members/Nobody in the address bar. The members come from members.csv, so anyone who joined last lesson is here too.

api.py
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.run()
Try it yourselfYour edits are saved in this browser.

InvestigateTrace how each line of code connects to what you see.

Investigate

api.py
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.run()

Why an app wants JSON.

What a program gets when nothing matches.

A function that was never run.

A weather app shows the club's members. Why would it use /api/members, not the home page?

What does /api/members/Nobody send?

Visiting /api/members gives 500 Internal Server Error. Click the line that is wrong.

ModifyChange the code on purpose and check the result.

Modify

Add /api/stats, which sends the number of members in each group.

Need a hint?

Count the members of each group in a dictionary (as in Data detective's bar chart) and return the dictionary.

Let /api/members?name=ad send only the members whose name contains ad (any capitals). The part after ? is the query string; request.args.get("name", "") reads it.

Need a hint?

An address can end with ?name=ad. request.args.get("name", "") gives "ad" (or "" if there is none).

Another hint

Keep the members whose name contains it: if name in m["name"].lower():

Try it yourselfYour edits are saved in this browser.

Add /api/stats, which sends how many members each group has.

    Run your program. This ticks when your code does all of the above.

    Let /api/members?name=ad find members whose name contains ad.

      Run your program. This ticks when your code does all of the above.

      MakeBuild something new using what you have learned. Expect a few tries; that is normal.

      A results API

      Make an API for results.csv: /api/results sends every row, and /api/leader sends the player with the most wins and how many. Send wins as numbers, not text.

      Try it yourselfYour edits are saved in this browser.

      Make an API for results.csv: /api/results with every row, and /api/leader with the player who has the most wins.

        Run your program. This ticks when your code does all of the above.

        Info

        Where this leads

        The optional last lesson builds the same API in FastAPI, where the types of each input are checked for you.

        Anyone who knows the address can read /api/members. What should a real club API think about?

        For teachers

        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. About 45 minutes.

        Open the teacher guide: answers, solutions, slides and a worksheet.

        Students will be able to

        Before class

        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.

        Running the lesson

        1. Predict: compare the dictionary in the code with the JSON on screen.
        2. Investigate: ask which is easier for a program: the home page HTML or the JSON?
        3. Modify: the stats route is Data detective's counting dictionary again; point that out to students who did it.
        4. Info: discuss what in members.csv should not be public (if it had emails or year levels).

        The hard part (on purpose)

        Seeing JSON as data for programs, not a page. Ask how another program would use the reply, before adding routes.

        When students get stuck

        Stretch and support

        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.

        Where it leads

        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.

        What each step is for

        Now you can
        Next lesson6. The same API in FastAPIBuild 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.