Skip to main content

Global vs. Local Blueprint Hooks

In this project, the Blueprint class provides two levels of hooks: local hooks that only trigger for routes defined within the blueprint, and global hooks that affect every request across the entire application.

Local Blueprint Hooks​

To execute logic only for requests handled by a specific blueprint, use the standard request decorators. These are useful for blueprint-specific setup, such as verifying a specific header or modifying the response of a specific module.

from flask import Blueprint

bp = Blueprint("bp", __name__)

@bp.before_request
def before_bp():
# This only runs for routes in 'bp'
pass

@bp.after_request
def after_bp(response):
# This only runs for routes in 'bp'
response.headers["X-Blueprint-Custom"] = "True"
return response

@bp.errorhandler(404)
def handle_local_404(e):
# This only catches 404s raised within 'bp' routes
return "Blueprint resource not found", 404

These hooks are merged into the application's hook dictionaries during registration via _merge_blueprint_funcs in src/flask/sansio/blueprints.py.

Global Application Hooks​

To influence the entire application from within a blueprint, use the app_ prefixed decorators. These are functionally equivalent to calling the corresponding methods on the Flask application object itself.

Global Request Processing​

A common use case is loading user data for every request in the application, regardless of which blueprint handles it. The following example from examples/tutorial/flaskr/auth.py demonstrates using before_app_request to manage authentication state globally.

from flask import Blueprint, g, session
from flaskr.db import get_db

bp = Blueprint("auth", __name__, url_prefix="/auth")

@bp.before_app_request
def load_logged_in_user():
"""If a user id is stored in the session, load the user object from
the database into ``g.user``."""
user_id = session.get("user_id")

if user_id is None:
g.user = None
else:
g.user = (
get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone()
)

Global Error Handling​

You can centralize error handling for the whole app within a specific blueprint using app_errorhandler. This is useful for creating a dedicated "errors" module.

errors_bp = Blueprint("errors", __name__)

@errors_bp.app_errorhandler(403)
def forbidden_handler(e):
return "you shall not pass", 403

# This handler will now catch 403 errors raised anywhere in the app,
# even in routes defined outside of 'errors_bp'.

Global Template Utilities​

Blueprints can also register utilities that become available in all Jinja templates across the application, not just those rendered by the blueprint's views.

bp = Blueprint("utils", __name__)

@bp.app_template_filter("reverse")
def reverse_filter(s):
return s[::-1]

@bp.app_context_processor
def inject_now():
import datetime
return {"now": datetime.datetime.utcnow()}

These methods (like app_template_filter and app_context_processor) use record_once internally to ensure that the utility is only registered with the application once, even if the blueprint is registered multiple times.

Registration Lifecycle and Restrictions​

Blueprint hooks are "deferred." When you use a decorator like @bp.before_app_request, the function is added to self.deferred_functions. These functions are only executed when app.register_blueprint(bp) is called.

Gotcha: Modifying Registered Blueprints​

Once a blueprint has been registered with an application, you cannot add new hooks or routes to it. Attempting to do so will raise an AssertionError.

# src/flask/sansio/blueprints.py
def _check_setup_finished(self, f_name: str) -> None:
if self._got_registered_once:
raise AssertionError(
f"The setup method '{f_name}' can no longer be called on the blueprint"
f" '{self.name}'. It has already been registered at least once..."
)

Ensure all decorators and setup logic are completed before calling app.register_blueprint().

Precedence and Overlap​

  • Templates: Blueprint templates have lower precedence than the application's main templates folder.
  • Static Files: If a blueprint does not have a url_prefix, the application's static route will take precedence over the blueprint's static files.
  • Multiple Registrations: If you register the same blueprint multiple times, global hooks (using app_ decorators) are only applied during the first registration due to the use of record_once.