Implementing websocket with FastAPI

In the previous tutorial, we built a boilerplate to serve HTML. In this tutorial, we are going to actually put javascript-based web socket calls from frontend to backend. Let's proceed with the backend first.

class ConnectionManager:
    def __init__(self) -> None:
        self.active_connections: list[str,WebSocket]= {}
        print("Creating a list to hold active connections",self.active_connections)

    async def connect(self, room_id: str, websocket: WebSocket):
        await websocket.accept()
        if not self.active_connections.get(room_id):
            self.active_connections[room_id] = []
        self.active_connections[room_id].append(websocket)
        print("New Active connections are ",self.active_connections)

    async def disconnect(self, room_id: str, websocket: WebSocket):
        self.active_connections[room_id].remove(websocket)
        print("After disconnect active connections are: ",self.active_connections)

    async def send_personal_message(self, message: str, websocket: WebSocket):
        await websocket.send_text(message)
        print("Sent a personal msg to , ",websocket)

    async def broadcast(self, message: str, room_id: str, websocket: WebSocket):
        for connection in self.active_connections[room_id]:
            if connection != websocket:
                await connection.send_text(message)
                print("In broadcast: sent msg to ",connection)

manager = ConnectionManager()


@app.websocket("/{room_id}")
async def websocket_chat(websocket: WebSocket, room_id: str):
    await manager.connect(room_id, websocket)
    try:
        while True:
            data = await websocket.receive_text()
            await manager.send_personal_message(f"You wrote: {data}",websocket)
            await manager.broadcast(f"A client says: {data}", room_id, websocket)
    except Exception as e:
        print("Got an exception ",e)
        await manager.disconnect(room_id, websocket)

The ConnectionManager class acts as a central hub for handling WebSocket connections. It has methods to connect, disconnect, send personal messages, and broadcast messages to connected clients. When an instance ConnectionManager is created, it initializes an empty dictionary called active_connections to keep track of connected clients.

The connect method of the ConnectionManager class is called when a new WebSocket connection is established. It takes a room_id and a websocket instance as parameters. This method accepts the new connection by sending an acceptance signal to the client using the await websocket.accept() call. It then adds the new WebSocket instance to the list of active connections for the specified room_id. If the room_id does not exist in the active_connections dictionary, a new entry is created.

Conversely, the disconnect method is responsible for handling WebSocket disconnections. It takes a room_id and a websocket instance as parameters and removes the WebSocket instance from the list of active connections for the specified room_id.

The send_personal_message method allows sending personalized messages to a specific client. It takes a message and a websocket instance as parameters and uses the WebSocket instance to send the message to that specific client.

The broadcast method is designed to send a message to all clients in a given room except the sender. It accepts a message, a room_id, and the sender's websocket instance. It iterates through the active connections for the specified room_id and sends the message to each connected client except the sender.

The code would be crystal clear if you type it out. Let's move forward to the WebSockets part now. and add the below code in templates/chatroom.html after the ul tag.

<!DOCTYPE html>
<html>
    <body>
        <!-- previous blog content here -->
        <ul id='messages'>
        </ul>
        <script>
            roomId = document.getElementById('room-id').textContent
            var ws = new WebSocket(`ws://localhost:8000/${roomId}`);
            console.log("Websocket endpoint is ",ws)
            ws.onmessage = function(event) {
                var messages = document.getElementById('messages')
                var message = document.createElement('li')
                var content = document.createTextNode(event.data)
                message.appendChild(content)
                messages.appendChild(message)
            };
            function sendMessage(event) {
                var input = document.getElementById("messageText")
                ws.send(input.value)
                input.value = ''
                event.preventDefault()
            }
        </script>
    </body>
</html>

The form is set to trigger the JavaScript function sendMessage(event) when it is submitted.  The JavaScript script section follows, responsible for establishing and managing the WebSocket connection. The script starts by extracting the room_id from the hidden <div> using JavaScript's document.getElementById('room-id').textContent. This value, representing the specific chat room, is then used to construct a WebSocket URL in the format ws://localhost:8000/${roomId}.

subsequently, a new WebSocket object (ws) is created using the constructed URL. This WebSocket connection acts as a conduit for real-time communication between the client (the user's web browser) and the server.

The script includes an event handler ws.onmessage, which is triggered whenever a new message is received over the WebSocket connection. Within this handler, the code dynamically creates new <li> elements to represent each incoming message. The content of the message is encapsulated within a text node, and this node is appended to the newly created <li>. Ultimately, the completed <li> element is added to the previously mentioned <ul> with the id "messages"
Lastly, the sendMessage function is defined. This function is called when the user submits a message through the form. It sends the entered message to the WebSocket server using the ws.send(input.value) method. Following the sending of the message, the function clears the input field to prepare for the next message, and it prevents the default form submission behavior using event.preventDefault(). This ensures that the form submission does not result in a full page reload, which is crucial for maintaining a seamless chat experience.

FastAPITutorial

Brige the gap between Tutorial hell and Industry. We want to bring in the culture of Clean Code, Test Driven Development.

We know, we might make it hard for you but definitely worth the efforts.

Contacts

Refunds:

Refund Policy
Social

Follow us on our social media channels to stay updated.

© Copyright 2022-23 Team FastAPITutorial