real-time-web
Skill1 starsChanged 3 months agoMITView on GitHub
Use when implementing real-time features with SSE, WebSocket, or HTMX integration in Lexigram
In this repository17 other files it gives its agents.
- ai-subsystem-quickstartSkill
- auth-and-securitySkill
- caching-patternsSkill
- cli-code-generationSkill
- cli-config-and-inspectSkill
- cli-database-operationsSkill
- cli-project-scaffoldingSkill
- configuration-managementSkill
- creating-providers-and-modulesSkill
- database-repository-patternSkill
- events-and-messagingSkill
- lexigram-uiSkill
- multimedia-generationSkill
- resilience-patternsSkill
- testing-with-lexigramSkill
- using-result-and-error-codesSkill
- web-controllers-and-routingSkill
The file
Files
Real-Time Web
Overview
Lexigram supports two real-time primitives: SSE (one-way server→client) and WebSocket (bidirectional). Both are class-based handlers registered by decorators. HTMX can consume SSE streams for declarative live updates.
SSE with HTMX
Subclass AbstractSSEHandler, decorate the class with @sse_endpoint(path), and override stream(request) as an async generator yielding event dicts:
from lexigram.web.sse import sse_endpoint
from lexigram.web.sse.handler import AbstractSSEHandler
from starlette.requests import Request
@sse_endpoint("/events/{channel}", heartbeat_interval=15)
class LiveFeedHandler(AbstractSSEHandler):
event_types = ["message"]
async def stream(self, request: Request):
channel = request.path_params["channel"]
async for update in subscribe(channel): # your pub/sub source
yield {"event": "message", "data": update}There is no push() — each client runs its own generator. To fan out to many clients, read from a shared async queue or pub/sub channel inside stream() (the built-in broadcast() raises NotImplementedError). Override on_connect/on_disconnect for lifecycle; max_connections caps concurrency.
HTMX client:
<div hx-sse="connect:/events/dashboard swap:message">
<div hx-get="/partials/dashboard" hx-trigger="load">Loading…</div>
</div>WebSocket Handler
@websocket_handler(path) decorates the handler class (not methods). Subclass AbstractWebSocketHandler and implement on_connect / on_message / on_disconnect; on_message receives an already-parsed JSON dict:
from lexigram.web.websocket.decorators import websocket_handler
from lexigram.web.websocket.handler import AbstractWebSocketHandler
@websocket_handler("/ws/chat/{room_id}")
class ChatHandler(AbstractWebSocketHandler):
async def on_connect(self, websocket):
await websocket.accept() # required
async def on_message(self, websocket, message: dict):
await self.broadcast(message) # or send per-connection
async def on_disconnect(self, websocket):
passFor raw receive loops use while True: message = await websocket.receive_text() — there is no iter_text() async iterator on the connection.
Common Mistakes
- Decorating methods instead of classes — both
@sse_endpointand@websocket_handlerwrap the class - Calling
push(...)orbroadcast(...)on SSE handlers — SSE is unidirectional; share a queue/pub-sub instream() - Forgetting
await websocket.accept()in WebSocketon_connect— connection never opens - Blocking the SSE generator with sync I/O — keep it async throughout
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

