fastapi-ws-batch
Batch and coalesce outgoing FastAPI WebSocket messages, instead of sending one message per event.
The problem
A naive real-time app sends a WebSocket message the instant something happens. If a client emits 30 updates a second (cursor moves, game ticks, live state changes), your server sends 30 separate messages a second — each with its own framing overhead — for just that one client. With more clients, that multiplies fast.
This is a real problem hit while building a real-time multiplayer game: naive per-event sends were flooding the socket. Batching outgoing messages into small windows (~50ms) instead of sending them immediately cut network traffic by roughly 80% in production, with no perceptible added latency.
fastapi-ws-batch packages that fix so you don't have to build it yourself.
Install
pip install fastapi-ws-batch
Usage
Before — one send per event:
@app.websocket("/ws/{room_id}")
async def websocket_endpoint(websocket: WebSocket, room_id: str):
await websocket.accept()
while True:
data = await websocket.receive_json()
await websocket.send_json(data) # sent immediately, every time
After — batched automatically:
from fastapi_ws_batch import BatchedConnection
@app.websocket("/ws/{room_id}")
async def websocket_endpoint(websocket: WebSocket, room_id: str):
await websocket.accept()
conn = BatchedConnection(websocket, flush_interval=0.05)
await conn.start()
try:
while True:
data = await websocket.receive_json()
await conn.add_event(data) # queued, flushed every 50ms as one batch
except WebSocketDisconnect:
pass
finally:
await conn.stop()
The receiving client now gets a batch of events every ~50ms instead of a message per event — same information, far fewer round-trips.
Two ways to send
add_event(data) — every message is kept, in order. Use this when every message matters (chat messages, discrete game actions).
add_latest(key, data) — only the newest value per key survives until the next flush; older ones for the same key are silently dropped. Use this for state where only the current value matters (a player's live position, a cursor location):
await conn.add_latest(player_id, {"x": 10, "y": 20})
If a player's position updates 20 times within a 50ms window, only the final position is actually sent — not all 20.
Early flush on bursts
conn = BatchedConnection(websocket, flush_interval=0.05, batch_size=50)
If 50 messages queue up before the next scheduled flush, it flushes immediately instead of waiting out the full interval — so a sudden burst doesn't sit buffered any longer than necessary.
How it works
add_event()/add_latest()queue messages instead of sending them right away.- A background task flushes the queue every
flush_intervalseconds, sending everything queued as one batch viasend_json. - If a failed send happens (e.g. the socket drops mid-send), the batch is restored to the queue instead of being lost, and retried on the next tick — unless something newer for the same key has already replaced it.
conn.stop()flushes anything left and stops the background task cleanly — always call it when the connection ends (e.g. in afinallyblock), so nothing queued is lost.
API
BatchedConnection(
websocket, # any object with an async send_json(dict) method
flush_interval, # seconds between automatic flushes
batch_size=None, # optional: force an early flush once this many messages queue up
)
await conn.start() # begin the background flush loop
await conn.add_event(data) # queue an in-order message
await conn.add_latest(key, data) # queue a message, keeping only the latest per key
await conn.stop() # flush remaining messages and stop cleanly
Why this exists
Existing FastAPI WebSocket libraries handle connection lifecycle and room/broadcast management well, but none of them batch or coalesce outgoing messages — every example sends immediately, per event. This library is the missing piece: put it in front of your existing send logic to cut traffic without changing your app's structure.
License
MIT
Release files for fastapi-ws-batch 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_ws_batch-0.1.0.tar.gz | 5.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_ws_batch-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 10.9 kB
Release files / fastapi_ws_batch-0.1.0.tar.gz
| Download URL | fastapi_ws_batch-0.1.0.tar.gz |
|---|---|
| Size | 5.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5767f46fd8b87ba6d05d9ecc247e9b5bd5ce8d3ccb62979fe0c3bd758ec2c0d7
|
|
BLAKE2b-256 checksum How to use checksums |
647fb4b50351af57892a6d7d574e2bf5e3a71663d82c1ff16e3987cf72e92f2a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Release files / fastapi_ws_batch-0.1.0-py3-none-any.whl
| Download URL | fastapi_ws_batch-0.1.0-py3-none-any.whl |
|---|---|
| Size | 5.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
66b5ed2d4517a835f3ff735a9fb860185752361c27c56d2c296542771590cb66
|
|
BLAKE2b-256 checksum How to use checksums |
77d570a9643c1cc0a3e32eb553c9b7b6442d9de0255c2ca29078bde063ce30e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|