0%

69- Introduction to Flask

Build web applications with Python. Create routes, handle requests, render templates, and serve JSON APIs. Your first step into web development.

You have written scripts. You have processed data. You have built scrapers. But your programs run only on your machine. What if you could share them with the world? A web application is the answer.
Flask is a micro web framework for Python. It is small, lightweight, and easy to learn. With Flask, you can turn any Python script into a web application. You can create APIs, websites, dashboards, and more.
This lesson introduces Flask fundamentals: routes to handle different URLs, request methods (GET, POST), rendering HTML templates, and returning JSON responses. By the end, you will have built your first web application and API.

🕯️ Magic Note

Flask is a “micro” framework because it keeps the core simple and extensible. It does not include a database abstraction layer or form validation by default. You add what you need. This makes Flask perfect for beginners and small to medium projects.

Installing Flask
Install Flask using pip.

Bash

pip install flask

Your First Flask Application
A minimal Flask app with a single route.

Python

from flask import Flask

# Create Flask application instance

app = Flask(__name__)

# Define a route (URL)

@app.route(“/”)

def home():

return “Hello, World!”

# Run the application

if __name__ == “__main__”:

app.run(debug=True)

💡 The @app.route() decorator tells Flask which URL triggers the function. When someone visits the root URL (“/”), Flask calls the home() function and returns the result.
Running the Flask Application
Run the script. Flask starts a development server.

Bash

python app.py

# Output:

# * Serving Flask app ‘app’

# * Debug mode: on

# * Running on http://127.0.0.1:5000

🕯️ Magic Note

With debug=True, the server automatically restarts when you change your code. This is invaluable during development. Never use debug=True in production.

Routes and Dynamic URLs
Create routes with variable parts.

Python

from flask import Flask

app = Flask(__name__)

@app.route(“/”)

def home():

return “Welcome to my site!”

# Dynamic route with variable

@app.route(“/user/<name>”)

def user_profile(name):

return f”Hello, {name}!”

# Dynamic route with type converter

@app.route(“/square/<int:num>”)

def square(num):

return f”{num} squared is {num ** 2}”

# Multiple variables

@app.route(“/add/<int:a>/<int:b>”)

def add(a, b):

return f”{a} + {b} = {a + b}”

💡 Type converters: string (default), int, float, path (accepts slashes), uuid. Use them to validate and convert URL parameters automatically.
HTTP Methods (GET, POST)
Handle different request methods on the same route.

Python

from flask import Flask, request

app = Flask(__name__)

@app.route(“/login”, methods=[“GET”, “POST”])

def login():

if request.method == “POST”:

# Handle form submission

username = request.form.get(“username”)

password = request.form.get(“password”)

# Validate credentials…

return f”Welcome back, {username}!”

else:

# Show login form

return ”’

<form method=”post”>

<input type=”text” name=”username” placeholder=”Username”><br>

<input type=”password” name=”password” placeholder=”Password”><br>

<button type=”submit”>Login</button>

</form>

”’

🕯️ Magic Note

The request object contains all data sent by the client: request.args for URL parameters, request.form for form data, request.json for JSON data, and request.headers for HTTP headers.

Accessing Request Data
Different ways to receive data from clients.

Python

from flask import Flask, request

app = Flask(__name__)

# URL parameters (/?name=Ali&age=25)

@app.route(“/search”)

def search():

name = request.args.get(“name”, “Guest”)

age = request.args.get(“age”, type=int)

return f”Searching for {name}, age {age}”

# Form data (POST with application/x-www-form-urlencoded)

@app.route(“/submit”, methods=[“POST”])

def submit():

email = request.form.get(“email”)

message = request.form.get(“message”)

return f”Received: {email} – {message}”

# JSON data (POST with application/json)

@app.route(“/api/data”, methods=[“POST”])

def api_data():

data = request.json

if not data:

return {“error”: “No JSON data”}, 400

return {“received”: data}

Returning JSON Responses
Flask automatically converts dictionaries to JSON responses.

Python

from flask import Flask, jsonify

app = Flask(__name__)

# Simple JSON API

@app.route(“/api/user/<int:user_id>”)

def get_user(user_id):

user = {

“id”: user_id,

“name”: “Feloriya”,

“age”: 25,

“skills”: [“Python”, “Web Design”]

}

return user # Flask automatically converts to JSON

# Using jsonify explicitly (more control)

@app.route(“/api/status”)

def status():

return jsonify({“status”: “ok”, “version”: “1.0”})

🕯️ Magic Note

When you return a dictionary from a Flask route, Flask automatically calls jsonify() on it. The response gets the correct Content-Type: application/json header.

Rendering HTML Templates
Use Jinja2 templates to separate HTML from Python logic.

HTML (templates/index.html)

<!DOCTYPE html>

<html>

<head>

<title>{{ title }}</title>

</head>

<body>

<h1>{{ message }}</h1>

<ul>

{% for item in items %}

<li>{{ item }}</li>

{% endfor %}

</ul>

</body>

</html>

Python

from flask import Flask, render_template

app = Flask(__name__)

@app.route(“/”)

def home():

data = {

“title”: “My Flask App”,

“message”: “Hello from Flask!”,

“items”: [“Python”, “Flask”, “Jinja2”]

}

return render_template(“index.html”, **data)

💡 Templates must be placed in a folder named templates in the same directory as your Flask app. Flask automatically looks for templates there.
Static Files (CSS, JavaScript, Images)
Serve static files from the static folder.

Directory Structure

my_app/

├── app.py

├── templates/

│ └── index.html

└── static/

├── css/

│ └── style.css

├── js/

│ └── script.js

└── images/

└── logo.png

HTML Template

<link rel=”stylesheet” href=”{{ url_for(‘static’, filename=’css/style.css’) }}”>

<img src=”{{ url_for(‘static’, filename=’images/logo.png’) }}” alt=”Logo”>

🕯️ Magic Note

The url_for() function generates URLs for routes and static files. It is safer than hard-coding URLs because it works even if your application is not mounted at the root.

Redirects and Error Handling
Redirect users and return custom error pages.

Python

from flask import Flask, redirect, url_for, abort

app = Flask(__name__)

@app.route(“/old-page”)

def old_page():

return redirect(url_for(“new_page”))

@app.route(“/new-page”)

def new_page():

return “You have been redirected!”

@app.route(“/secure/<int:user_id>”)

def secure(user_id):

if user_id != 1:

abort(403) # Forbidden

return “Welcome, admin!”

# Custom error handler

@app.errorhandler(404)

def not_found(error):

return {“error”: “Page not found”}, 404

Practical Example: Todo List API
Build a complete REST API for managing todo items.

Python

from flask import Flask, request, jsonify

from datetime import datetime

app = Flask(__name__)

# In-memory database

todos = []

next_id = 1

@app.route(“/api/todos”, methods=[“GET”])

def get_todos():

“””Get all todo items.”””

return jsonify(todos)

@app.route(“/api/todos”, methods=[“POST”])

def create_todo():

“””Create a new todo item.”””

global next_id

data = request.json

if not data or “title” not in data:

return {“error”: “Title is required”}, 400

todo = {

“id”: next_id,

“title”: data[“title”],

“description”: data.get(“description”, “”),

“completed”: data.get(“completed”, False),

“created_at”: datetime.now().isoformat()

}

todos.append(todo)

next_id += 1

return jsonify(todo), 201

@app.route(“/api/todos/<int:todo_id>”, methods=[“GET”])

def get_todo(todo_id):

“””Get a single todo by ID.”””

todo = next((t for t in todos if t[“id”] == todo_id), None)

if todo is None:

return {“error”: “Todo not found”}, 404

return jsonify(todo)

@app.route(“/api/todos/<int:todo_id>”, methods=[“PUT”])

def update_todo(todo_id):

“””Update a todo item.”””

todo = next((t for t in todos if t[“id”] == todo_id), None)

if todo is None:

return {“error”: “Todo not found”}, 404

data = request.json

if “title” in data:

todo[“title”] = data[“title”]

if “description” in data:

todo[“description”] = data[“description”]

if “completed” in data:

todo[“completed”] = data[“completed”]

return jsonify(todo)

@app.route(“/api/todos/<int:todo_id>”, methods=[“DELETE”])

def delete_todo(todo_id):

“””Delete a todo item.”””

global todos

todo = next((t for t in todos if t[“id”] == todo_id), None)

if todo is None:

return {“error”: “Todo not found”}, 404

todos = [t for t in todos if t[“id”] != todo_id]

return “”, 204

if __name__ == “__main__”:

app.run(debug=True)

Practical Example: Simple Calculator Web App
A calculator that handles GET requests with query parameters.

Python

from flask import Flask, request, render_template_string

app = Flask(__name__)

HTML_TEMPLATE = ”’

<!DOCTYPE html>

<html>

<head><title>Calculator</title></head>

<body>

<h1>Simple Calculator</h1>

<form method=”get”>

<input type=”number” name=”a” placeholder=”First number” value=”{{ a }}” required>

<select name=”op”>

<option value=”add” {{ “selected” if op == “add” }}>+</option>

<option value=”sub” {{ “selected” if op == “sub” }}>-</option>

<option value=”mul” {{ “selected” if op == “mul” }}>×</option>

<option value=”div” {{ “selected” if op == “div” }}>÷</option>

</select>

<input type=”number” name=”b” placeholder=”Second number” value=”{{ b }}” required>

<button type=”submit”>Calculate</button>

</form>

{% if result is not none %}

<h2>Result: {{ result }}</h2>

{% endif %}

{% if error %}

<p style=”color: red”>{{ error }}</p>

{% endif %}

</body>

</html>

”’

@app.route(“/”)

def calculator():

a = request.args.get(“a”, type=float)

b = request.args.get(“b”, type=float)

op = request.args.get(“op”)

result = None

error = None

if a is not None and b is not None and op:

if op == “add”:

result = a + b

elif op == “sub”:

result = a – b

elif op == “mul”:

result = a * b

elif op == “div”:

if b == 0:

error = “Cannot divide by zero”

else:

result = a / b

else:

error = “Invalid operation”

return render_template_string(

HTML_TEMPLATE,

a=a, b=b, op=op, result=result, error=error

)

if __name__ == “__main__”:

app.run(debug=True)

Common Mistakes with Flask
  • Forgetting to import request, render_template, etc.
  • Using debug=True in production (security risk)
  • Not handling POST data correctly (request.form vs request.json)
  • Hard-coding URLs instead of using url_for()
  • Not returning proper HTTP status codes (200, 404, 500)
  • Storing files in the wrong directories (templates, static)
Check Your Understanding
  • How do you create a route that handles both GET and POST requests?
  • Write a route that accepts a variable username from the URL and returns a greeting.
  • How do you access JSON data sent in a POST request?
  • What is the purpose of the url_for() function?
  • Where should HTML templates be stored in a Flask project?
  • Write a route that returns a JSON response with a 404 status code.

⚡ Whisper

Flask turns Python into the web. A few lines of code become a server. A route becomes a page. A function becomes an API endpoint. The web is not magic. It is requests and responses. Flask handles both. You define routes. You write functions. You return strings, templates, or JSON. The development server runs. You visit http://localhost:5000. Your code comes alive. This is not just a framework. It is a gateway. From scripts to services. From local to global. From your machine to the world. Learn Flask. Build a blog. Create an API. Deploy your first web app. The web is waiting. Your code is ready.

Related posts