Skip to main content

Request Pre and Post-processing Hooks

Flask provides a set of decorators to hook into the request lifecycle, allowing you to execute code before a request is handled, after a response is generated, or when a request context is torn down. These hooks are defined in the Scaffold class, which serves as the base for both the Flask application object and Blueprint objects.

Executing Code Before the Request

When you need to perform actions like loading a user from a session or checking permissions before every request, use the before_request decorator.

from flask import Flask, g, session

app = Flask(__name__)

@app.before_request
def load_user():
if "user_id" in session:
# Assume db.get_user is a helper function
g.user = db.get_user(session["user_id"])

Short-circuiting the Request

If a before_request function returns a non-None value, Flask treats that value as the response and stops further processing. This means the view function and any subsequent before_request handlers will not be called.

@app.before_request
def check_maintenance_mode():
if is_maintenance_mode():
return "Service Unavailable", 503

Internally, Flask.preprocess_request iterates through the registered functions in self.before_request_funcs. It calls the application-level hooks first, followed by hooks for the active blueprint.

Modifying the Response

To modify the response object after the view function has executed, use the after_request decorator. This is commonly used to add custom headers or security policies to every response.

@app.after_request
def add_security_headers(response):
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "SAMEORIGIN"
return response

Requirements and Execution Order

  • Return Value: An after_request function must receive a response object and must return a response object (either the same one or a new one).
  • Reverse Order: Functions registered with after_request are executed in reverse order of registration. If you register A then B, B will run first.
  • Exceptions: These hooks are only called if the request was handled successfully without an unhandled exception. If an exception occurs, use teardown_request for cleanup.

In Flask.process_response, Flask first executes hooks added dynamically via after_this_request, then blueprint-specific hooks, and finally application-wide hooks.

Cleaning Up Resources

The teardown_request decorator registers functions to run when the request context is popped. This happens at the very end of a request, even if an unhandled exception occurred. This makes it the ideal place for resource cleanup, such as closing database connections.

@app.teardown_request
def close_db_connection(exception=None):
db.close()
if exception:
app.logger.error(f"Request failed with: {exception}")

Key Characteristics

  • Exception Handling: The function receives one argument: the exception that caused the request to fail, or None if it succeeded.
  • Guaranteed Execution: Unlike after_request, teardown functions are called even if a before_request hook short-circuited the request or if the view raised an error.
  • Reverse Order: Like after_request, these run in reverse order of registration.
  • Reliability: Flask's do_teardown_request method wraps each call in a try/except block (via _CollectErrors) to ensure that one failing teardown function does not prevent others from running.

Blueprint-Specific vs. Global Hooks

Blueprints allow you to define hooks that only apply to the routes they manage.

  • Blueprint-only: @bp.before_request only runs for requests handled by that blueprint.
  • Application-wide from Blueprint: @bp.before_app_request (and its counterparts after_app_request and teardown_app_request) registers the hook on the application itself, affecting every request regardless of which blueprint handles it.
auth_bp = Blueprint("auth", __name__)

@auth_bp.before_request
def check_auth_token():
# Only runs for routes in auth_bp
...

@auth_bp.before_app_request
def global_init():
# Runs for every request in the app
...

Asynchronous Hooks

Flask supports asynchronous request hooks. You can define any of these hooks as async def, and Flask will handle the execution using ensure_sync.

@app.before_request
async def async_before():
await some_async_setup()

@app.after_request
async def async_after(response):
await some_async_logging(response)
return response

When an async hook is called, Flask ensures it is executed within the appropriate context, allowing you to use await for I/O-bound tasks during the request lifecycle.