Server-Sent Events (SSE)

For one-way updates (notifications, progress bars, live dashboards) SSE is simpler than WebSockets: plain HTTP, automatic reconnection in the browser and it passes through Nginx and HTTP/2. Use WebSockets when the browser also needs to send messages.

1. A stream from a generator

Python
import asyncio
from lila.core.sse import EventStream

@router.get("/import/progress")
async def import_progress(request: Request):
    async def steps():
        for pct in range(0, 101, 10):
            yield {"event": "progress", "data": {"pct": pct}}
            await asyncio.sleep(0.5)
        yield {"event": "done", "data": {"message": "Import finished"}}
    return EventStream(request, steps())

2. Channels: publish from anywhere

Python
from lila.core.sse import Channel
from lila.core.session import Session

notifications = Channel("notifications")

@router.get("/events", auth=True)
async def events(request: Request):
    user = request.state.user
    return notifications.stream(request, key=f"user:{user['id']}")   # broadcasts + this user's messages

# in a route, a task or a scheduled job:
await notifications.publish({"message": "New comment"}, event="notice")                 # everyone
await notifications.publish({"message": "Invoice paid"}, event="notice", to="user:7")   # one user

3. Browser

HTML
<!-- No JavaScript: append messages, show toasts -->
<ul data-sse="/events" data-sse-event="notice" data-sse-swap="append" data-sse-toast="News"></ul>

<!-- Progress bar -->
<div id="bar" class="h-2 bg-blue-600" style="width:0"></div>
<script>
  var progress = lilaSSE("/import/progress");
  progress.on("progress", (d) => { document.getElementById("bar").style.width = d.pct + "%"; });
  progress.on("done", (d) => { lila("toast", { title: "OK", description: d.message, type: "success" }); progress.close(); });
</script>