Flask Signals
Signals allow different parts of a Flask application to communicate without tight coupling. When something important happens — a request starts, a user logs in, a template renders — Flask sends a signal. Any part of your code can subscribe to that signal and run a function in response.
The Event Notification Analogy
Think of signals like a newsletter. The newsletter publisher (Flask) sends an issue when something happens. Subscribers (your listener functions) receive it and react however they want. The publisher does not know or care who subscribes — it just sends the signal.
Flask sends: request_started signal
│
┌────────────┼────────────┐
▼ ▼ ▼
Log the request Track analytics Check rate limit
(listener 1) (listener 2) (listener 3)
Flask's Built-In Signals
Flask uses the blinker library for signals. Install it first:
pip install blinker| Signal | Fired When |
|---|---|
request_started | Before every request is dispatched |
request_finished | After a request is handled (before response sent) |
request_tearing_down | After request teardown |
got_request_exception | When an unhandled exception occurs |
template_rendered | After a template is rendered |
appcontext_pushed | When an app context is pushed |
Subscribing to a Signal
from flask import request
from flask.signals import request_started, got_request_exception
def log_request(sender, **kwargs):
print(f'[REQUEST] {request.method} {request.path}')
def log_exception(sender, exception, **kwargs):
print(f'[ERROR] Unhandled exception: {exception}')
# Connect listeners
request_started.connect(log_request, app)
got_request_exception.connect(log_exception, app)The sender argument is the Flask app that fired the signal. Always pass app as the second argument to .connect() so your listener fires only for your app (not for all Flask apps in the process).
Creating Custom Signals
Define your own signals for application-level events:
from blinker import Namespace
my_signals = Namespace()
user_registered = my_signals.signal('user-registered')
order_placed = my_signals.signal('order-placed')
payment_failed = my_signals.signal('payment-failed')Sending a Custom Signal
@app.route('/register', methods=['POST'])
def register():
user = create_new_user(request.form)
db.session.commit()
# Send signal with the new user as extra data
user_registered.send(app, user=user)
return redirect(url_for('login'))Listening to a Custom Signal
@user_registered.connect_via(app)
def send_welcome_email(sender, user, **kwargs):
# Send a welcome email to the new user
send_email(to=user.email, subject='Welcome!', body='Thanks for joining.')
@user_registered.connect_via(app)
def assign_default_role(sender, user, **kwargs):
user.role = 'member'
db.session.commit()
@user_registered.connect_via(app)
def log_registration(sender, user, **kwargs):
app.logger.info('New user registered: %s', user.email)Three listeners subscribe to the same signal. All three fire when a user registers. The registration route does not need to know about emails, roles, or logging — it just sends the signal.
Signals vs Before/After Request Hooks
| Feature | Request Hooks | Signals |
|---|---|---|
| Scope | Every request | Specific events you define |
| Multiple subscribers | One function per hook | Unlimited subscribers |
| Decoupling | Moderate | High |
| Return value | Can return a response | Cannot return a response |
Summary
Flask signals let your application broadcast events without the broadcaster knowing who listens. Use built-in signals to hook into Flask's lifecycle (request start, exceptions, template rendering). Create custom signals for domain events like user registration, order placement, or payment success. Multiple independent listeners can subscribe to the same signal, keeping business logic decoupled and easy to extend without modifying existing code.
