Skip to content

Latest commit

History

20 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

MongoDB Change Broadcaster

PyPI VersionPython Versions

A real-time change stream broadcaster for MongoDB, supporting multiple delivery channels (WebSocket, Redis, HTTP, etc.) with extensible architecture.

Read the blog post on the implementation here.

Building a Versatile Data Streaming System with Broadcaster Package

Features

  • 📡 Listen to MongoDB change streams
  • 🚀 Built-in channels: WebSocket, Redis, HTTP, and Database Logging
  • 🔌 Extensible architecture for custom channels
  • ⚡ Async-first implementation
  • 🛠️ Configurable pipelines and filtering

Recent Changes (most recent)

  • WebSocketChannel: Added a ping/pong monitor to detect dead clients (PING_INTERVAL = 60s), plus configurable timeout and disconnect_on_timeout to automatically drop stale connections. The channel now supports an authenticate coroutine (return (bool, client_id)) to accept and assign client IDs, and replaces existing connections for the same client ID (closes old socket and cancels its ping task). Sending uses safe JSON serialization and includes robust disconnect handling on send failures.

  • MongoChangeBroadcaster: Added a custom_fn hook that can transform ChangeEvent objects before delivery. Collection watchers now run with an exponential backoff retry (via tenacity) for resiliency, and the broadcaster validates the Mongo URI before connecting. You can filter by field-level changes with fields_to_watch, and extract recipients using dot-notation paths (e.g. owner.id) for targeted delivery. Start/stop flows and error logging were also improved.

Installation

pip install mongo-broadcaster
# Optional dependencies for specific channels:pip install mongo_broadcaster[fastapi] # WebSocketpip install mongo_broadcaster[redis] # Redis Pub/Sub support

Basic Usage

frommongo_broadcasterimport (
MongoChangeBroadcaster,
BroadcasterConfig,
CollectionConfig
)
frommongo_broadcaster.channelsimportWebSocketChannel# Initialize with MongoDB connectionconfig=BroadcasterConfig(
mongo_uri="mongodb://localhost:27017",
collections=[
CollectionConfig(
collection_name="users",
fields_to_watch=["name", "email"],
recipient_identifier="fullDocument._id"
)
]
)
broadcaster=MongoChangeBroadcaster(config)
broadcaster.add_channel(WebSocketChannel())
# Start listening (typically in your app startup)awaitbroadcaster.start()

Built-in Channels

ChannelDescriptionIdeal For
WebSocketChannelReal-time browser updatesLive dashboards
RedisPubSubChannelPub/Sub messagingMicroservices
HTTPCallbackChannelWebhook notificationsThird-party integrations
DatabaseChannelPersistent change loggingAudit trails

Extending with Custom Channels

Implement your own channel by subclassing BaseChannel:

frommongo_broadcaster.channels.baseimportBaseChannelfromtypingimportAny, DictclassCustomMQTTChannel(BaseChannel):
def__init__(self, broker_url: str):
self.broker_url=broker_urlself.client=Noneasyncdefconnect(self):
"""Initialize your connection"""self.client=awaitsetup_mqtt_client(self.broker_url)
asyncdefsend(self, recipient: str, message: Dict[str, Any]):
"""Send to specific recipient"""awaitself.client.publish(f"changes/{recipient}", message)
asyncdefbroadcast(self, message: Dict[str, Any]):
"""Send to all subscribers"""awaitself.client.publish("changes/all", message)
asyncdefdisconnect(self):
"""Clean up resources"""awaitself.client.disconnect()
# Usage:broadcaster.add_channel(CustomMQTTChannel("mqtt://localhost"))

Configuration Options

CollectionConfig

CollectionConfig(
collection_name: str,
database_name: Optional[str] =None,
# Fields to include in change eventsfields_to_watch: List[str] = [],
# Dot-notation path to identify recipients (e.g., "fullDocument._id")recipient_identifier: Optional[str] =None,
# MongoDB change stream optionschange_stream_config: ChangeStreamConfig=ChangeStreamConfig()
)

Examples

FastAPI WebSocket Endpoint

fromfastapiimportFastAPI, WebSocketapp=FastAPI()
ws_channel=WebSocketChannel()
@app.websocket("/ws/{client_id}")asyncdefwebsocket_endpoint(websocket: WebSocket, client_id: str):
awaitws_channel.connect(client_id, websocket)
try:
whileTrue:
awaitwebsocket.receive_text()
exceptWebSocketDisconnect:
awaitws_channel.disconnect(client_id)

Please see the examples folder for more.

Contributing

To add new channels:

  1. Create a subclass of BaseChannel
  2. Implement required methods:
  • connect()
  • send()
  • broadcast()
  • disconnect()
  1. Submit a PR!

License

MIT

TODO

  • Write tests

About

Broadcast MongoDB change streams to WebSocket, Redis, HTTP, and more

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages