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.