How to use Flask-SocketIO to create real-time applications (WebSockets) with Python

Video thumbnail

Flask-SocketIO is a Flask extension that allows the integration of WebSockets into a Flask application, enabling real-time, bidirectional communication between the server and client without the need for constant page refreshes. For example, in a chat application, you send messages to the server, to another person or group of people; however, it can also receive messages. Therefore, in the classic client-server model, it's not possible to retrieve messages in the same way you send them.

Consequently, you would have to reload the page or send requests to the server every few changes to check for messages. But with full-duplex communication, there's a channel between the server and the client (the reverse), and there's no need to send requests to the server periodically (which is an inefficient approach).

If you've ever wanted to send and receive messages without having to reload the page, Flask-SocketIO is the ideal tool for you. This Flask extension allows you to add real-time (full-duplex) communication to your Python projects with surprising ease.

When I developed a chat system with Flask, I realized that traditional HTTP wasn't enough: I needed a live channel between the server and the client that would allow me to send data instantly. That's where Flask-SocketIO came in.

We agreed that we know how to implement role-based access control in Flask.

We left off with learning how to use a role system in Flask.

What is Flask-SocketIO and what is it for?

Flask-SocketIO is an extension that integrates WebSockets within a Flask project, allowing permanent bidirectional communication between the server and browsers.

Flask-SocketIO uses a WebSocket implementation from the JavaScript Socket.IO library, which is compatible with multiple browsers and supports the creation of rooms to handle multiple simultaneous connections. Additionally, it provides functions for sending messages to the client and receiving messages from the client in real-time.

Communication between clients and servers is part of any application available on the Internet today. Web sockets are a protocol developed to facilitate this process. They allow us to establish bidirectional contact between a client (or several clients) and a server. You can think of your web browser as a client, for example. The server can be a background program running on your system.

WebSocket is a communication protocol for the client-server model; to understand WebSocket, it is best to compare the specialty of WebSocket over HTTPS.

http vs websocket
http vs websocket


There are some advantages of WebSocket over HTTPS, but they mainly involve the possibility of creating a full-duplex channel between the client and server, and the possibility of creating rooms to host clients, create broadcast messages, among others.

Using WebSockets is very easy in Flask, as with other possible dependencies, we can install a plugin with which to have complete control of WebSockets in Flask and the enablement of a special server to be able to use WebSockets.

In this post, we will see how we can create rooms to communicate several clients with each other, with messages to the server and vice versa.

Differences between HTTP and WebSocket

HTTP follows the classic pattern: the client requests and the server responds.
WebSocket, on the other hand, maintains an open channel that allows the server to also send data without waiting for a request.

In my experience, this completely changes the flow of an app: when one user sends a message, everyone else receives it in real-time, without reloading or checking every second.

⚡ Advantages of full-duplex communication

  • Instant data updates.
  • Lower server load (no constant polling).
  • Ideal for chats, dashboards, games, streaming, or notifications.

Installation and initial configuration

️ Create virtual environment and install Flask-SocketIO

As always, I recommend working in an isolated environment in a virtual environment for Flask:

$ python3 -m venv flask_socket_env
$ source flask_socket_env/bin/activate
$ pip install flask flask-socketio

️ Base project structure
/flask_socket_app

├── app.py
└── templates/
   └── index.html

⚙️ Socket server configuration

from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'clave_secreta_segura'
socketio = SocketIO(app)
@app.route('/')
def index():
   return render_template('index.html')
if __name__ == '__main__':
   socketio.run(app, debug=True)

How Flask-SocketIO works

Events, emit, and listening

Flask-SocketIO works through events: the server listens and the client emits.
For example:

@socketio.on('mensaje')
def handle_message(data):
   print('Mensaje recibido:', data)
   socketio.emit('mensaje', data)

Creating our Room

The use of rooms is a mechanism that allows us to have greater control over the users who can connect to our duplex channels from the client and the server; unlike the communication we have in Flask SocketIO in which we do not have this control, since any user who enters the section of our website where we enable the sockets, as shown in the video on the cover. They will have total access to them without any control; but with rooms we can now indicate which user is going to join or leave a room; this gives us much more control over which user can or cannot access to see the messages we share in that room; for this, we add one more parameter to the communication both on the server and on the client when emitting events:

emit('chat', message['message'], broadcast=True, to=message['room'])
socket.emit('chat', { message: message.value.trim(), room:"room{{current_user.id}}"})

Connecting and disconnecting

In Flask Socket IO, we can easily detect when we connect and disconnect from the socket server; for that:

@socketio.on('connect')
def connect():
   print("Conect!")
@socketio.on('disconnect')
def disconnect():

Joining and leaving rooms

Therefore, now we are going to use two more functions to be able to join a room or leave it.

To join a room:

join_room 

And to leave a room:

leave_room

Create a real-time chat with Flask-SocketIO

️ Server: handle events and messages

The chat function, which we use to send messages from the server; that is, Flask:

@socketio.on('chat')
def chat(message):
   print("Chat:", message)
   socketio.emit('chat', message['message'], broadcast=True, to=message['room'])

In this function we emit an event, passed from the client and with broadcast we indicate that all clients who are listening to the message['room'] room will receive this message; this function is used from the client as we will see in a moment.

Client: emit and receive messages

And from the client, that is, JavaScript:

<textarea id="message"></textarea>
<button onclick="sendMsg()">Enviar</button>

<script>
  const socket = io();
  function sendMsg() {
    const message = document.querySelector("#message").value.trim();
    if (!message) return alert("No hay mensaje que enviar");
    socket.emit('chat', { message: message, room: "room1" });
  }

  socket.on('chat', msg => console.log("Nuevo mensaje:", msg));
</script>

In this function we perform the typical checks to see if we have data, after this, using the emit function, we emit the event to the chat function hosted on the server, the one we saw previously.

Server

Returning to the server, it is important to note that, to use a socket, we need to join and also be able to leave that group; as we mentioned before, for this there are the join_room and leave_room functions, in which, with the session enabled, we join or leave the group respectively.

Finally, the functions we use to join or leave the group:

@socketio.on('join')
def join(room):
    username=current_user.name   
    print("Join")
    join_room(room['room'])
    emit('join', username+" se unio a la habitacion", to=room['room'])
@socketio.on('leave')
def leave(room):
    username=current_user.name   
    print("leave")
    leave_room(room['room'])
    emit('leave', username+" abandono la habitacion", to=room['room'])

As you can see, to make it a little more interesting, we use Flask Login to have different data for each user who makes a connection.

The room parameter can be taken from anywhere, a reference that comes from the view, database, etc; in this example we have it set by default from the client's chat function.

With this, we can send messages that only users who are assigned to the room called "room" after prior access will hear; for that we have a couple of buttons:

<button onclick="join()">Unir</button>
<button onclick="leave()">Abandonar</button>

And their corresponding functions:

function join(){
     socket.emit('join', { room:"room{{current_user.id}}"})
}
function leave(){
  socket.emit('leave', { room:"room{{current_user.id}}"})
}

Complete step-by-step example

These implementations are ideal for chat-type or live collaboration applications.

Finally, I leave you the complete code that is part of my complete Flask course with Python:

from flask import Blueprint,render_template
from flask_socketio import emit, join_room, leave_room
from flask_login import login_required, current_user
from app import db, socketio
from app.user.models import User
from app.chat.models import MessageRoom
from datetime import datetime
roomBp = Blueprint('room',__name__)
@roomBp.route("/room")
@login_required
def index():
    return render_template("room/index.html")
@socketio.on('chat')
@login_required
def chat(message):    
    print("Estamos en evento "+str(message))
    emit('chat', message['message'], broadcast=True, to=message['room'])
@socketio.on('join')
@login_required
def join(room):
    username=current_user.name   
    print("Join")
    join_room(room['room'])
    emit('join', username+" se unio a la habitacion", to=room['room'])
@socketio.on('leave')
@login_required
def leave(room):
    username=current_user.name   
    print("leave")
    leave_room(room['room'])
    emit('leave', username+" se unio a la habitacion", to=room['room'])

Authentication with Flask-Login

I used Flask-Login for authenticated users to be able to join rooms.

This adds a real layer of security to the chat, something that many guides omit.

Control who sees what messages

By using rooms, you can decide which users receive each message, segmenting private or group conversations easily.

In the chat view:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sokect en Flask</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"
        integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA=="
        crossorigin="anonymous"></script>
</head>
<body>
    <textarea id="message"></textarea>
    <button onclick="sendMsj()">Enviar</button>
    <button onclick="join()">Unir</button>
    <button onclick="leave()">Abandonar</button>
    <script>
        var socket = io()
        function join(){
            socket.emit('join', { room:"room{{current_user.id}}"})
        }
        function leave(){
            socket.emit('leave', { room:"room{{current_user.id}}"})
        }
        function sendMsj() {
            message = document.querySelector("#message")
            if (message.value.trim() == "")
                return alert("No hay mensaje que enviar")
            socket.emit('chat', { message: message.value.trim(), room:"room{{current_user.id}}"})
            message.value = ""
        }
        socket.on('connect', function () {
            console.log("Conectados!")
        })
        socket.on('disconnect', function () {
            console.log("Desconectados!")
        })
        socket.on('chat', function (message) {
            console.log("chat "+message)
        })
        socket.on('leave', function (message) {
            console.log("leave "+message)
        })
        socket.on('join', function (message) {
            console.log("join "+message)
        })
    </script>
    <h1>Hola Mundo</h1>
</body>
</html>

⚙️ Good practices and production deployment

Eventlet and Gunicorn

For production, use Eventlet or Gunicorn with asynchronous workers:

$ pip install eventlet
$ socketio.run(app, host='0.0.0.0', port=5000, debug=False)

Security and session management

  • Use a secure SECRET_KEY.
  • Integrate role control with custom decorators.
  • Isolate rooms and avoid unnecessary global emissions.

Scalability and monitoring

Flask-SocketIO can scale using Redis as a message backend and Nginx load balancers.
In my experience, this is essential if you plan to handle tens of simultaneous users.

Conclusion

Flask-SocketIO converts a standard Flask application into an interactive and lively system.
You don't need complex infrastructures: a Python server and a few lines are enough to have real-time communication.

In my experience, it is one of the simplest and most powerful ways to take a Flask project to the next level.
If you are interested in delving deeper, I recommend practicing with rooms, authentication, and deployment with Eventlet: three pillars that make the difference between a demo and a professional app.

❓ Frequently Asked Questions (FAQs)

  • Does Flask-SocketIO replace WebSocket?
    • No. Flask-SocketIO implements WebSocket, but adds a layer of compatibility and much simpler event control.
  • Can I use Flask-SocketIO with React or Vue?
    • Yes. You only need the corresponding Socket.IO client (socket.io-client) in your frontend app.
  • What to do if events are not emitted?
    • Verify that you use the same event name on the client and server, and that your application runs with socketio.run() and not with app.run().

I agree to receive announcements of interest about this Blog.

We will see how to use the rooms in Flask SocketIO that allow us to create groups of full duplex connections for users.

| 👤 Andrés Cruz

🇪🇸 En español