Flask Blueprints, Modular Architecture & Extension Authoring
Large-scale enterprise Flask applications are organized using Blueprints. A Blueprint defines a modular collection of routes, templates, static assets, error handlers, and middleware that can be registered on a Flask application instance. Furthermore, understanding how to author reusable Flask Extensions using the init_app pattern is a fundamental skill for senior developers.
This chapter details Flask Blueprint architecture, URL prefixing, Blueprint-specific middleware (before_request), CLI command integration (app.cli.command), and custom extension design patterns.
1. Blueprint Architecture & Modular Routing
Instead of registering all routes directly on app, encapsulate related domain logic (e.g., auth, payments, admin) into independent Blueprints:
Flask Blueprint Modular Architecture:
Flask Application (app)
βββ Register: auth_bp (url_prefix="/api/v1/auth")
β βββ POST /login
β βββ POST /logout
βββ Register: payments_bp (url_prefix="/api/v1/payments")
β βββ POST /charge
β βββ GET /invoices
βββ Register: admin_bp (url_prefix="/admin")
βββ GET /dashboard# app/auth/routes.py
from flask import Blueprint, jsonify, request
# Create Blueprint instance
auth_bp = Blueprint("auth", __name__, template_folder="templates")
@auth_bp.route("/login", methods=["POST"])
def login():
data = request.get_json()
# Handle login logic...
return jsonify({"token": "jwt_token_value"})# app/__init__.py
def create_app() -> Flask:
app = Flask(__name__)
from app.auth.routes import auth_bp
# Register Blueprint with URL prefix
app.register_blueprint(auth_bp, url_prefix="/api/v1/auth")
return app2. Blueprint Middleware Scope (before_request)
Flask provides two levels of middleware hooks:
@bp.before_request: Runs ONLY for incoming requests that match routes inside that specific Blueprint!@app.before_request: Runs globally for EVERY incoming request across all registered Blueprints.
# Admin Blueprint Security Middleware
admin_bp = Blueprint("admin", __name__)
@admin_bp.before_request
def verify_admin_role():
# Only executes for routes matching /admin/*
if not current_user.is_admin:
return jsonify({"error": "Admin access required"}), 4033. Custom Flask CLI Commands (app.cli.command)
Extend Flaskβs flask command-line interface using Click decorators:
import click
from flask import Flask
def register_commands(app: Flask):
@app.cli.command("seed-db")
@click.option("--count", default=10, help="Number of seed users to create.")
def seed_db(count: int):
"""Seed database with mock user records."""
with app.app_context():
create_mock_users(count)
click.echo(f"Successfully created {count} mock users!")# Execute custom CLI command
flask seed-db --count 504. Authoring Reusable Flask Extensions
To create a custom open-source Flask extension (e.g. Flask-Metrics), follow the standard extension design pattern:
# flask_metrics.py
from flask import Flask, g
import time
class FlaskMetrics:
def __init__(self, app: Flask = None):
if app is not None:
self.init_app(app)
def init_app(self, app: Flask):
"""Lazy extension initialization."""
app.before_request(self._before_request)
app.after_request(self._after_request)
# Store extension instance in app.extensions dict
if not hasattr(app, "extensions"):
app.extensions = {}
app.extensions["metrics"] = self
def _before_request(self):
g._start_time = time.perf_counter()
def _after_request(self, response):
total_time = time.perf_counter() - getattr(g, "_start_time", time.perf_counter())
response.headers["X-Response-Time-Ms"] = f"{total_time * 1000:.2f}"
return response