CS1999 Buggy Racing tech notes

Technical notes for working on your CS1999 Buggy Editor project

How Flask works as a webserver

Make sure you've seen the description of how a webserver works, because this page follows from that one. In that example, the webserver's response to each request is to send back the contents of one of the files it has in its "server root" directory. That's the simplest way to understand how webservers work (and it is useful: it's how webservers handle static content, which is a big part of the web).

In that example, this is the process that the webserver follows each time a request arrives:

  1. determine what resource the request is for (by looking at the route or path of the URL)
  2. find the file corresponding to that route
  3. read it
  4. send its contents back (as the body of the response)

Flask replaces that with a very similar process. But instead of finding a file, it finds the method to use to generate the response (again, by looking at the route, or path, that is in the URL). It runs that method, which ultimately produces a something (usually text, such as HTML) to send back as the body of the response. For an HTML page, that text is a description of the contents of the page.

At this point, it's no longer accurate to say that the request is for a file — which is why the things being requested are called resources. That's what the R in URL stands for: it's a Universal Resource Locator (a descriptor that identifies how to find a specific resource, anywhere on the world-wide web).

In the simplest case, the text really could just be "Hello world":

@app.route("/")
def welcome():
    return "Hello world!"

The @app.route() line is a Python decorator telling Flask to run this method if the incoming request's route matches /.

The route / matches no path, so http://example.com/ (or http://example.com) would respond with "Hello world!" (and a status of 200success), but http://localhost:5000/something would not, because there's no route for something. (In that case, Flask's would send a response with the status code 404not found).

Your Flask app isn't running on example.com: depending on how you're running it, it's probably something like http://localhost:5000 or some other number — the port number is after the colon (:). See more about localhost.

Typically, it's more complicated than that, and the method involves some form of processing. It might do a calculation and send the result back, or read the data from the database. Very often that result must be an HTML page, so it's common for the process to consist of gathering or calculating information, getting a template HTML file, and putting the results into that template. Taking the template and populating it with the information to produce a string is called rendering the template. The animated diagram below demonstrates this process.

The Python library Flask manages this webserver functionality. It listens to the port for requests, matches incoming requests with the right methods (using @route), and — where necessary — renders HTML templates.

Not just getting, but also posting

In the original example, the process was effectively just operating as a file server. But Python is a general programming language, so you can perform arbitrarily complex processes to determine what response should be sent back.

Those processes might actually do things, before sending the response back. That is, a request need not just be to GET something. For example, in the buggy editor you can POST data for it to save in the database (maybe the number of wheels). The Flask app does that task, and the response it sends back depends on whether or not it succeeded.

In fact there are other methods (sometimes called "verbs") besides GET and POST that you can use to make web requests.

Webserver application (such as Flask)

The animated diagram below shows a how a request triggers the webserver application to construct the response — using a database and an HTML template. Compare this with the diagram on the tech note showing the webserver serving files. In the buggy racing project, the application is Flask. Flask uses the route to determine which method in app.py to run — that method returns an HTTP response, which (in this example) is an HTML page.

  1. The example starts with a call to www.example.com/buggy, which will display a page showing how many wheels the racing buggy has.
  2. The client (browser) sends a GET request for the resource (/buggy).
  3. The webserver application (the buggy editor app.py) scans for a route that matches the path in the request: /buggy. The first method it finds is show_buggies() so that's what it will run to generate its response.
  4. The show_buggies() method reads a record from the database. It looks for the buggy with id 1, and the record it finds has a quantity of wheels (qty_wheels) with a value of 6.
  5. The show_buggies() method renders the template found in buggy.html. It's mostly HTML with some extra instructions, including a placeholder where the number of wheels should go. The record from the database is used to populate it.
  6. Finally, show_buggies() returns the rendered template as a string containing HTML. The app sends that back as the payload of its response, with a status code of 200 ("success"), and a header indicating this is HTML.
  7. The browser receives the repsonse, and renders the page described by the HTML, which displays the number of wheels the buggy has.
An interactive version of this diagram is available with JavaScript enabled.

MIME-types

In the webserver-as-fileserver example, we glossed over how the client knows what kind of content is in the payload of the repsonse, because it's implicit in the filename in the request (cat.html is a webpage, cat.jpg is an image). But really that only works for humans, so in fact any response that has a payload always includes a header (Content-type) whose value is tells the client what media type it contains. Now you're seeing requests which don't have an implicit file extension, you can see why these are crucial to how the web works (otherwise, how does the browser know if the response to /buggy contains text describing a web page about a buggy, or an image of a buggy?).

What are the limits?

Although it's true that the method you run in response to a request could be arbitrarily complex, if it takes too long, the client (that is, the browser that issued the request) may time out and give up waiting for a response.

Busy webservers also have the problem of dealing with hundreds or even thousands of requests coming in every second — this can have similarly bad consequences if the responses cannot be handled promptly.

The are many techniques for dealing with these things — one you have probably encountered already is cacheing (see the tech note about how to bust the CSS cache). But this is also an example of why just being able to write source code is not enough. Your buggy editor is running a relatively simple task for a single, infrequent user (you), so it's a very forgiving environment in which to practice your web development. But professional web developers may be working on apps that need to do a lot of calculation, handle high volumes of traffic, or make many database reads. Ultimately, this is why it's not enough to be able to simply write source code — you need to understand not only the language (in this case Python) but also the systems it is interacting with, and know how to write code that uses all these components efficiently.