March 4, 2026
·—Building a Database-Driven Real-Time Notification Protocol with Flask and WebSockets
How I built a middleware-based protocol that lets a server communicate with sections of itself in real-time, using MongoDB, Flask-SocketIO, and a pre/post middleware pipeline.
Introduction
The core problem is deceptively simple. When something changes in the database, how does the rest of the system know about it immediately? Not on the next poll. Not on the next page refresh. Right now. And more importantly, how does it know in a way that is controlled, secure, and composable?
That is what this project solves. It is a small, self-contained protocol that wires together MongoDB, Flask-SocketIO, and a pre/post middleware pipeline to create a communication channel between the database layer and the transport layer of a server.
The full source code is available on GitHub.
Why "Database Notification System"?
The distinction matters. A typical app notification system is surface-level: a user does something, the server pushes a message to a client. The database might be involved as storage, but it is not the source of truth driving the event.
In a database notification system, the database mutation itself is the trigger. The act of inserting a document is what kicks off the entire downstream chain. The middleware wrapping the insert is what timestamps the record, generates a secure one-time token, and pushes the event over WebSockets to the correct connected client.
Think of it less as "send a notification to a user" and more as "the server's database layer is telling the server's transport layer that something happened, and the transport layer is forwarding that to whoever is listening." It is an internal communication protocol that happens to surface its results to a browser.
This pattern gives you a few things that a naive notification system does not:
- Atomicity: The notification only fires if the database write succeeds, because the post-middleware runs after the insert.
- Security: Each notification event is wrapped in a signed, short-lived JWT data token that gets verified before delivery.
- Targeting: The WebSocket layer matches the notification to the specific connected client whose authentication token corresponds to the intended recipient.
- Composability: The middleware pipeline is modular. You can add new pre-insert or post-insert steps without touching the rest of the system.
Architecture Overview
Here is the full data flow, from the initial HTTP request to the browser rendering the notification:
POST /send
|
v
send_notification(user_id)
|
v
insert_one_with_middleware(data)
|
|=> pre_middleware : adds notification_created_at timestamp
|=> MongoDB insert : persists the notification document
|=> post_middleware : creates a NotificationDataToken,
| signs a JWT around it, and emits
| a 'generate' event to the WebSocket
| server via an internal async client
v
Notifications namespace (on_generate)
|
|=> verifies the data token JWT
|=> looks up the notification record
|=> finds the connected client whose JWT matches
| notification_for
|=> emits 'new_notification' to that client's room
v
Browser receives the event and renders it instantly
The key insight is the internal async SocketIO client in the post-middleware. After a successful database insert, the middleware does not try to find and emit to a connected client directly. Instead, it acts as a client itself, emitting a generate event to the server's own WebSocket namespace. The namespace handler then does the lookup, verification, and targeted delivery. This separation keeps the database layer and the WebSocket layer cleanly decoupled.
File Structure
The codebase is organized to keep concerns separated:
app.py Flask + SocketIO server
seed.py Seeds a demo user, prints JWT
requirements.txt
models/
db.py Shared MongoClient
Notification.py Notification schema + collection
NotificationDataToken.py One-time data token model
User.py Minimal user model
middlewares/
pre/insert_one/Notification.py Adds timestamp
post/insert_one/Notification.py Token + WS push
functions/insert_one_with_middleware/
Notification.py Composes pre -> insert -> post
websockets/
namespaces/Notifications.py SocketIO namespace handling
connect / generate / disconnect
helpers/
verify_jwt.py Verifies a user JWT against the DB
notifications/
send_notification.py High-level "send a notification" function
verify_notification_data_token.py Validates a one-time data token
templates/
index.html Browser test client
The middlewares/ directory mirrors a pattern you may recognize from Mongoose in the Node.js ecosystem: hooks that run before and after specific database operations. Except here it is built from scratch in Python, tailored to work with PyMongo and Flask-SocketIO.
The Middleware Pipeline
The pipeline is the heart of the system. When insert_one_with_middleware is called, three things happen in sequence:
1. Pre-middleware runs before the database write. In this case, it injects a notification_created_at timestamp into the document. In a production system, this is where you would also run validation, sanitization, or enrichment logic.
2. The MongoDB insert happens next. The document is persisted. If this step fails, the post-middleware never runs, which means no phantom notifications get pushed to clients for data that does not actually exist.
3. Post-middleware runs after a successful insert. This is where the interesting work happens. It creates a NotificationDataToken (a one-time-use token tied to the specific notification document), wraps it in a signed JWT, and then emits a generate event to the WebSocket server using an internal async SocketIO client.
On the WebSocket side, the Notifications namespace receives the generate event, verifies the data token JWT, looks up the notification from the database, finds the connected client whose session JWT matches the notification_for field, marks the data token as used, and emits new_notification to that specific client's room.
The one-time data token is a deliberate security measure. Even if someone intercepts the internal generate event, the token can only be used once and is validated against the database before any notification is delivered.
Use Cases
This pattern is more versatile than it might appear at first glance. Anywhere you need a server to internally react to its own state changes and propagate those changes outward, this pipeline fits:
- Live Dashboards: An admin inserts a new record or updates a metric. The middleware pipeline detects the change and pushes updated data to every connected dashboard client without polling.
- Collaborative Applications: When one user modifies a shared resource, the post-middleware can notify all other connected users who are viewing that resource, enabling real-time collaboration without a separate pub/sub layer.
- Internal Microservice Event Propagation: Services within a monolith (or even across a network) can use the middleware-to-WebSocket bridge to signal state changes to other parts of the system.
- Admin Monitoring Panels: Critical events (failed transactions, threshold breaches, new signups) trigger immediate visual feedback on a monitoring screen the moment the data hits the database.
- IoT Data Pipelines: Sensor data lands in MongoDB, and the middleware pipeline immediately pushes it to a visualization layer for real-time charting.
- Order and Transaction Tracking: E-commerce or fintech systems where the user needs to see status changes (payment confirmed, order shipped) the instant they are recorded.
The common thread in all of these is that the database write is the source of truth, and the notification is a side effect of that write, not a separate action triggered by application logic.
Disclaimer
This project is intentionally kept as barebones as possible. It is a stripped-down extract from production systems I have built, with all the production-specific scaffolding removed. There is no caching layer, no Gunicorn or other WSGI/ASGI server configuration for managing WebSocket connections at scale, no load balancing, no horizontal scaling strategy, no rate limiting, and no retry/dead-letter logic for failed deliveries.
All of that exists in the production versions, but including it here would defeat the purpose. The goal of this repository is to isolate the core pattern (middleware-driven database notifications over WebSockets) in a way that is easy to read, easy to understand, and easy to drop into other projects as a foundation. If you are building something that needs real-time reactivity tied to database state, this is the starting point, not the finish line.