python4 min read

Flask Tutorial: Build Lightweight Web Apps from Scratch (2026)

Flask Tutorial: Build Lightweight Web Apps from Scratch (2026)

Published:  |  Category: Python  |  Reading time: ~15 min
Flask Tutorial: Build Lightweight Web Apps from Scratch (2026)

I reached for Flask for the first time when I needed to spin up a tiny webhook receiver over lunch. The entire app fit in one file, and the lack of prescribed structure felt liberating after larger frameworks. Flask gives you routing, request handling, and templating with Jinja2 — and then gets out of your way. You bring your own database library, your own forms, your own authentication. That minimalism is exactly right when you want full control or when the problem is small enough that convention adds friction.

This tutorial follows the path I take when starting a new Flask project: a single-file prototype for an API that manages a to-do list, then refactoring into a modular structure as it grows. You'll see request parsing, JSON responses, database integration with SQLAlchemy, and how to structure larger apps using Flask blueprints.

A Minimal Flask Application

A Flask app can be as short as a few lines. You instantiate the Flask class, define routes with the @app.route decorator, and run the development server. The route connects a URL pattern to a function that returns a response — either a string or a tuple of (body, status_code). This simplicity makes Flask ideal for microservices and small APIs where you don't want an ORM or admin panel.

from flask import Flask, jsonify

app = Flask(__name__)

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

if __name__ == '__main__':
    app.run(debug=True)

Request Parsing and URL Parameters

Incoming data arrives via request.args for query strings, request.json for JSON bodies, and request.form for form-encoded payloads. Flask parses these into Python dicts automatically. For route parameters, you wrap variable parts of the URL in angle brackets — enforces type conversion. This keeps URL routing clean without regex.

from flask import request

@app.route('/items', methods=['GET', 'POST'])
def handle_items():
    if request.method == 'POST':
        data = request.get_json()
        return jsonify({"received": data}), 201
    category = request.args.get('category', 'all')
    return jsonify({"category": category})

@app.route('/items/')
def get_item(item_id):
    return jsonify({"item_id": item_id})

Templates with Jinja2

Flask bundles Jinja2 as its template engine. Templates live in a templates/ directory and support template inheritance, filters, and control flow. I use Jinja2 for server-rendered HTML pages where I need SEO or fast initial load. The render_template function loads a template file and passes context variables that become accessible inside the template as {{ var }}.

from flask import render_template

@app.route('/profile/')
def profile(username):
    return render_template('profile.html', user=username, joined=2024)


{{ user }}

Joined {{ joined }}

{% if joined < 2025 %}

Veteran member

{% endif %}

Integrating SQLAlchemy for Database Access

Flask doesn't ship with a database layer — you add Flask-SQLAlchemy, which wraps SQLAlchemy into a Flask-friendly extension. Define models as classes inheriting from db.Model, then create and query them inside request handlers. The session management happens automatically per request, so you don't worry about connection pooling or transactions.

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy(app)

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    done = db.Column(db.Boolean, default=False)

@app.route('/items')
def list_items():
    items = Item.query.filter_by(done=False).all()
    return jsonify([{"id": i.id, "name": i.name} for i in items])

Blueprints for Modular Structure

As your app grows beyond a single file, blueprints let you split routes into separate modules. Each blueprint is a self-contained set of routes, templates, and static files. You register it with the main app, optionally with a URL prefix. This pattern keeps the codebase navigable and lets multiple developers work on different blueprints without merge conflicts.

# auth/views.py
from flask import Blueprint

auth_bp = Blueprint('auth', __name__, url_prefix='/auth')

@auth_bp.route('/login', methods=['POST'])
def login():
    return {"status": "logged in"}

# app.py
from auth.views import auth_bp
app.register_blueprint(auth_bp)

Error Handlers and Testing

Custom error handlers let you return JSON or render error pages for specific HTTP status codes. Flask's test client simulates requests without running a server, making it easy to write unit tests for your routes. I always add a 404 handler for JSON APIs so consumers get a structured error instead of raw HTML.

@app.errorhandler(404)
def not_found(e):
    return jsonify({"error": "Resource not found"}), 404

@app.errorhandler(500)
def server_error(e):
    return jsonify({"error": "Internal server error"}), 500

# Testing
import unittest
class TestApp(unittest.TestCase):
    def test_home(self):
        with app.test_client() as client:
            resp = client.get('/')
            self.assertEqual(resp.status_code, 200)

Frequently Asked Questions

Is Flask suitable for large production applications?

Yes, with the right structure. Blueprints, application factories, and extensions make Flask scalable. Many large sites run Flask — just be prepared to make architecture decisions yourself since Flask doesn't enforce them.

How do I handle database migrations in Flask?

Use Flask-Migrate, which wraps Alembic. It auto-generates migration scripts from your model changes and applies them to the database. Run flask db init, then flask db migrate, and flask db upgrade.

What's the difference between Flask and FastAPI?

Flask is synchronous by default and uses WSGI. FastAPI is asynchronous, uses ASGI, generates OpenAPI docs automatically, and leverages Pydantic for data validation. If you need async performance or auto-docs, choose FastAPI. If you prefer simplicity and ecosystem maturity, choose Flask.

Does Flask support WebSockets?

Not natively, but Flask-SocketIO adds WebSocket support using Socket.IO. For raw WebSocket performance, consider an ASGI framework like FastAPI or aiohttp instead.

Originally published on Ayodhyyya. Last updated June 1, 2026.