Backend Step 2: Hello Flask

Now you'll create your first Flask web server and understand how routes work.

What You'll Learn

  • Running a Flask server
  • Defining routes with @app.route()
  • Returning JSON with jsonify()
  • The request-response cycle

Step 1: Create the Basic Flask App

Edit backend/app/__init__.py:

from flask import Flask, jsonify


def create_app():
    app = Flask(__name__)

    @app.route("/")
    def home():
        return jsonify({"message": "Hello Flask!"})

    return app


if __name__ == "__main__":
    app = create_app()
    app.run(debug=True, port=5000)

Step 2: Run the Server

Choose the method that matches your setup from Step 1:

Option A — Virtual Environment:

cd aiproject/backend
source venv/bin/activate
python app/__init__.py

Option B — Docker:

cd aiproject/backend
docker compose up

You should see:

 * Serving Flask app 'app'
 * Debug mode: on
 * Running on http://127.0.0.1:5000

Step 3: Test It

Open a new terminal and run:

curl http://127.0.0.1:5000/

Expected output:

{"message":"Hello Flask!"}

Or open http://127.0.0.1:5000/ in your browser.

Step 4: Add a Status Route

Let's add a health check endpoint. Update __init__.py:

from flask import Flask, jsonify


def create_app():
    app = Flask(__name__)

    @app.route("/")
    def home():
        return jsonify({"message": "Hello Flask!"})

    @app.route("/api/status")
    def status():
        return jsonify({"status": "ok", "version": "1.0"})

    return app


if __name__ == "__main__":
    app = create_app()
    app.run(debug=True, port=5000)

Test it:

curl http://127.0.0.1:5000/api/status
# → {"status":"ok","version":"1.0"}

Step 5: Understanding Routes

A route is a URL pattern. When a request comes in, Flask matches the URL to the correct function:

@app.route("/")              # GET /  → calls home()
@app.route("/api/status")    # GET /api/status → calls status()

The function returns data, and jsonify() converts it to JSON.

How Flask Works

  1. You start the server with app.run()
  2. Flask listens on port 5000 for incoming HTTP requests
  3. When a request arrives, Flask matches the URL to a route
  4. Flask calls the matching function
  5. The function returns a response (JSON)
  6. Flask sends the response back to the client

Step 6: Lint Your Code (Catch Errors Early)

Now run ruff to check your code for issues:

cd aiproject/backend
ruff check app/

If everything is clean, you'll see no output. That's good — it means no issues found.

Try making a mistake on purpose — add an unused import, then run ruff again:

import os  # ← This line is never used
ruff check app/
# → app/__init__.py:1:1: F401 `os` imported but unused

Ruff caught the unused import. Remove it and run ruff again — clean.

Why this matters: Linting catches bugs before you even run the code. A missing import, a typo in a variable name, an unused function — ruff finds them all. Professional developers run linters before every commit.

Step 7: Write Your First Automated Test

So far you've been testing with curl — typing commands manually. That works, but it's slow and easy to forget.

Instead, write an automated test that checks the same thing:

Create backend/tests/test_status.py:

def test_status_returns_ok(client):
    response = client.get("/api/status")
    assert response.status_code == 200
    assert response.get_json() == {"status": "ok"}

This test: 1. Sends a GET to /api/status 2. Checks the status code is 200 (success) 3. Checks the JSON body is {"status": "ok"}

Run it:

cd aiproject/backend
pip install pytest
python -m pytest tests/ -v

Expected output:

tests/test_status.py::test_status_returns_ok PASSED

One test passed. You now have an automated test that you can run in 1 second instead of manually curling.

Step 8: Experiment

@app.route("/api/greet/<name>")
def greet(name):
    return jsonify({"message": f"Hello, {name}!"})

Test: curl http://127.0.0.1:5000/api/greet/Alice → {"message":"Hello, Alice!"}

Explanation: Why jsonify()?

Flask can return strings or HTML directly, but our mobile app needs JSON. jsonify() converts Python dictionaries into JSON format and sets the right Content-Type header.

Without jsonify():

return {"message": "hello"}  # In older Flask, this didn't work!

With jsonify():

return jsonify({"message": "hello"})  # Proper JSON response

Verification

curl http://127.0.0.1:5000/
# → {"message":"Hello Flask!"}

curl http://127.0.0.1:5000/api/status
# → {"status":"ok","version":"1.0"}

If both work, you're ready for Step 3.