Fixing Live Chat: How EditSync Switched from Polling to Server-Sent Events

Posted on September 17, 2025 by dreyster

The Problem: Our "Live" Chat Wasn't Truly Live

 

At EditSync, seamless collaboration is everything. When we first built our project chat, we used a common technique called long polling. The client would ask the server, "Any new messages?" every few seconds. If there were none, the server would wait a bit before responding. It worked, but it wasn't perfect.

Users noticed a slight delay between sending a message and seeing it appear for others. This lag, while small, interrupted the natural flow of conversation. Behind the scenes, this method was also inefficient, creating a constant hum of unnecessary network requests, which would only get worse as we scaled. We knew we could do better.


 

Our Solution: Server-Sent Events (SSE)

 

Instead of the client constantly asking for updates, we wanted the server to push them the moment they happened. We found the perfect tool for the job: Server-Sent Events (SSE).

SSE is a simple but powerful technology that allows a server to maintain a one-way, persistent connection with a client. Once the connection is open, the server can send data whenever it wants. This completely eliminates the need for repeated polling.

 

How We Implemented SSE

 

Our backend is built with Python and Flask, and the switch was surprisingly straightforward. Here’s a look at the core components.

1. The Backend Stream Endpoint

First, we created a dedicated endpoint for clients to subscribe to. When a user opens a chat, their browser connects to /api/chats//stream.

On the server, we maintain a dictionary of message queues, one for each active chat room.

 

Python
 
# A dictionary to hold message queues for each chat room
CHAT_ROOM_STREAMS = defaultdict(list)

@app.route("/api/chats//stream")
@login_required
def stream_chat_messages(chat_id):
    # ... authorization checks ...

    def event_stream():
        # Create a new queue for this specific client
        q = Queue()
        CHAT_ROOM_STREAMS[chat_id].append(q)
        
        try:
            # Loop indefinitely, waiting for a message
            while True:
                message = q.get() # This line waits...
                yield f"data: {json.dumps(message)}\n\n"
        finally:
            # Clean up when the user disconnects
            CHAT_ROOM_STREAMS[chat_id].remove(q)

    # Return the streaming response
    return Response(event_stream(), mimetype='text/event-stream')

 
 
 
 
 
 

When a client connects, we add their unique queue to the list for that chat room. The q.get() function pauses the execution until a message is added to the queue, making it highly efficient.

2. Pushing the Message

When a user sends a new message via a POST request to /api/chats//messages, we first save it to the database. Then, we push that new message data into every active queue for that chat room.

 

Python
 
@app.route("/api/chats//messages", methods=["POST"])
@login_required
def api_chat_messages(chat_id):
    # ... save message to database ...
    
    # After saving, get the message data
    message_data = { "id": str(new_message.id), ... }
    
    # Put the message onto every listening queue
    for q in CHAT_ROOM_STREAMS[chat_id]:
        q.put(message_data)
        
    return jsonify(message_data), 201

 

This instantly "wakes up" all the waiting event_stream functions, which then yield the new message down to every connected client.

3. The Frontend Connection

The client-side code is the simplest part. Using the native EventSource API in JavaScript, we just listen for messages.

 

JavaScript
 
// Connect to the stream for a specific chat room
const eventSource = new EventSource(`/api/chats/${chatId}/stream`);

// This function runs every time the server sends a message
eventSource.onmessage = function(event) {
    const message = JSON.parse(event.data);
    
    // Code to append the new message to the chat window
    displayNewMessage(message); 
};

 

That's it! No more manual polling, setInterval, or complex logic. The browser handles the connection automatically.


 

The Results

 

The switch to Server-Sent Events was a game-changer for EditSync's chat feature:

  • Instantaneous Delivery: Messages now appear in real-time. ⚡

  • Reduced Server Load: We eliminated thousands of unnecessary polling requests, making our application faster and more scalable.

  • Improved User Experience: The chat feels fluid and reliable, enabling effortless collaboration for our users.

By moving from a "pull" (polling) to a "push" (SSE) model, we delivered the truly live experience our users expect.