WebSocket Chat Example

A real-time chat application with room support, user presence, and message history.

Application Code

# app.py
import json
from datetime import datetime
from flaxon import Flaxon, WebSocket, WebSocketDisconnect

app = Flaxon("chat-app", debug=True)

# In-memory storage
rooms = {}
users = {}
message_history = {}
max_messages_per_room = 100

@app.websocket("/ws/chat/<room_id>")
async def chat(socket: WebSocket, room_id: str):
    username = socket.scope.get("query_string", "").split("=")[1] if "user=" in socket.scope.get("query_string", "") else "Anonymous"
    user_id = f"{username}-{id(socket)}"

    await socket.accept()
    await socket.join(room_id)

    if room_id not in rooms:
        rooms[room_id] = []
        message_history[room_id] = []

    rooms[room_id].append(user_id)
    users[user_id] = {"username": username, "room": room_id, "socket": socket}

    # Notify others
    await socket.broadcast_json(room_id, {
        "type": "system",
        "event": "user_joined",
        "user": username,
        "users": rooms[room_id],
        "timestamp": datetime.now().isoformat(),
    })

    # Send message history
    await socket.send_json({
        "type": "system",
        "event": "history",
        "messages": message_history[room_id][-max_messages_per_room:],
    })

    try:
        async for message in socket.iter_json():
            if message.get("type") == "message":
                msg = {
                    "type": "message",
                    "user": username,
                    "content": message.get("content"),
                    "timestamp": datetime.now().isoformat(),
                }
                message_history[room_id].append(msg)
                await socket.broadcast_json(room_id, msg)

            elif message.get("type") == "typing":
                await socket.broadcast_json(room_id, {
                    "type": "typing",
                    "user": username,
                    "is_typing": message.get("is_typing", False),
                })

    except WebSocketDisconnect:
        pass
    finally:
        users.pop(user_id, None)
        if room_id in rooms:
            rooms[room_id] = [u for u in rooms[room_id] if u != user_id]

        await socket.leave(room_id)

        await socket.broadcast_json(room_id, {
            "type": "system",
            "event": "user_left",
            "user": username,
            "users": rooms.get(room_id, []),
        })

@app.get("/rooms")
async def list_rooms():
    return {
        "rooms": [
            {
                "name": room_id,
                "users": len(users_in_room),
                "messages": len(message_history.get(room_id, [])),
            }
            for room_id, users_in_room in rooms.items()
        ]
    }

@app.get("/rooms/<room_id>/messages")
async def get_messages(room_id: str):
    return {"messages": message_history.get(room_id, [])}

@app.get("/rooms/<room_id>/users")
async def get_users(room_id: str):
    if room_id in rooms:
        usernames = [users.get(u_id, {}).get("username", "Unknown") for u_id in rooms[room_id]]
        return {"users": usernames}
    return {"users": []}
Key Concepts
  • WebSocket Rooms: Separate chat rooms
  • User Presence: Join/leave notifications
  • Message History: Store last 100 messages
  • Typing Indicators: Real-time typing status