2 · Flask: your first app
Flask is a tiny, readable web framework: `Flask(__name__)` creates the app, `@app.route("/")` maps a URL to a function, and the function's return value becomes the response. With a few lines and `app.run(debug=True)` you have a live server you can hit in a browser.
Flask maps URLs to Python functions: `app = Flask(__name__)` creates the app, the `@app.route("/path")` decorator registers a handler for that URL, and whatever the handler RETURNS becomes the response body (a string → HTML, a dict → JSON). `app.run(debug=True)` starts a dev server with auto-reload and in-browser error pages.
Without this:
Without a framework you'd hand-write raw socket and HTTP-parsing code for every route; Flask collapses all of that into one decorator per URL.
Now that you know the request/response round-trip, let's wire one up. Flask is a minimal Python web framework — small enough to read in an afternoon, powerful enough to serve real ML APIs. The entire mental model is: map a URL to a function, return a value, and Flask handles the HTTP.
Create the app. app = Flask(__name__) builds the application object. The __name__ argument tells Flask where the app lives (which module) so it can find templates and static files relative to your code. You'll create exactly one app per project.
Routes map URLs to functions. The @app.route("/") decorator says "when a request comes in for this URL, call the function below me." The function is the handler (also called a view function). Its return value becomes the response body:
- Return a string → Flask sends it as HTML with a
200 OK.return "Hello!"literally renders "Hello!" in the browser. - Return a dict → Flask serializes it to JSON automatically and sets the right
Content-Type.return {"status": "ok"}sends{"status":"ok"}as JSON. (This is the seed of the prediction API you'll build next chapter.)
URL parameters capture parts of the path. A route like @app.route("/user/<int:user_id>") captures a number from the URL and passes it to your function as an argument. A request to /user/42 calls profile(user_id=42). The <int:...> converter both extracts the value and enforces its type — /user/abc won't match an int route at all (Flask returns 404), so bad paths never reach your code.
The dev server + debug mode. app.run(debug=True) starts Flask's built-in development server, typically on http://127.0.0.1:5000. debug=True is your friend while building: it auto-reloads the server whenever you save a file (no manual restart), and it shows a rich, interactive traceback in the browser when something errors — instead of a blank "500" you see the exact line that broke.
One critical caveat: debug=True and the built-in server are for development only. The debugger exposes an interactive console an attacker could exploit, and the dev server isn't built for real load. In production you turn debug OFF and run behind a real WSGI server like gunicorn (covered later in this track / the MLOps track).
The two read-along cells below show a complete minimal app and the command that runs it.
A complete minimal Flask app: `Flask(__name__)`, three routes (string → HTML, dict → JSON, an `<int:user_id>` URL parameter), and `app.run(debug=True)`.
Install Flask and start the dev server — either `python app.py` or `flask --app app run --debug` — then open `http://127.0.0.1:5000` in a browser.
In Flask, what does the `@app.route("/predict")` decorator do?
- `app = Flask(__name__)` creates the application, and the `@app.route("/path")` decorator maps a URL to a handler function.
- A handler's return value becomes the response body: a string is sent as HTML, a dict is auto-serialized to JSON; `<int:id>` converters capture and type-check URL parameters.
- `app.run(debug=True)` starts the dev server with auto-reload and in-browser tracebacks — for development ONLY; turn debug off and use a real WSGI server in production.
The Flask app object and `@app.route` handlers are the skeleton every model API hangs on — the `/predict` endpoint you build next chapter is just one more route returning a dict.
If you remove it: Without Flask's routing you'd be parsing raw HTTP by hand for every endpoint; the framework is what lets you focus on the prediction instead of the plumbing.