In this article

Why make your application real-time?

Most applications deal with create/read/update/delete (CRUD) operations and support multiple instances of applications running at the same time. In order to reflect the changes as a result of these CRUD operations, we want to refresh (re-render) the page with the latest view. To enable this, we need WebSockets to emit and broadcast some kind of message from the API layer to all the instances of the application, and hence refresh the view or perform some action based on that broadcasted message in the UI.

Scenario

Let's say we have an application that is basically listing some users. It has a page that displays all the existing users and allows us to create a new user. When a new user is created, all open instances (i.e. browser tabs, different users on different workstations) of the application that has the users page open should display the new user that was created. The screenshot below is what the app looks like.

new application user

Before getting into details on SocketIO implementation, get access to code for the application via our GitHub repository. On the very root, there is a README.md file with details on how to install and run the application locally.

Broadcasting and consuming messages using sockets

Server-side 

  • Install Flask-SocketIO under the project folder (under API folder for this application) using command pip3 install flask-socketio or just run pip3 install -r requirements.txt in case you have it added under requirements.txt.
  • On the very top level under the project folder, the following lines within app.py will initialize and add Flask-SocketIO to the existing Flask application.
from flask_socketio import *

socketio = SocketIO(app, cors_allowed_origins="*")

if __name__ == '__main__':
    socketio.run(app, debug=True)
  • The following SocketIO events within app.py will listen to the events emitted by the client which will then broadcast the message back to the client. In the case shown below, the @socketio.on('UserAdded') event will listen to any incoming event emitted from the client and emit/broadcast a message back, which can then be consumed by the client using the event name 'userAddedResponse' and perform the required action, which in our case would be to re-render the page with the newly added user.
# SocketIO Events
@socketio.on('connect')
def connected():
    print('Connected')

@socketio.on('disconnect')
def disconnected():
    print('Disconnected')

@socketio.on('UserAdded')
def userAdded(message):
    print('User Added')
    emit('userAddedResponse', {'data': message}, broadcast=True)

Client-side

  • Install socket.io-client under the project folder (under client folder for this application), by running the command npm install socket.io-client or just run npm install if you have the socket.io-client dependency added to your package.json file.
  • The following code under client/src/App.js will connect to the server-side socket that we just implemented above using Flask-socketIO.
import io from 'socket.io-client';

export const socket = io('http://localhost:5000/');
  • In the following code under client/src/components/CreateNewUser.js, function 'handleSubmit' is called to add a user using axios.post which, when successful, uses socket.emit to emit 'UserAdded' event to the API along with metadata for the newly added user. The API then emits an event 'userAddedResponse'  and broadcasts a message with the new user's metadata in order for the client to consume it and re-render as shown in the next step.
import { socket } from '../App'
	
export default class CreateNewUser extends React.Component {
	handleSubmit = event => {
   		event.preventDefault();
	    const user = {
   	    	firstName: this.state.firstName,
        	lastName: this.state.lastName,
        	office: this.state.office,
        	email: this.state.email,
        	mobile: this.state.mobile
    	};

	    axios.post(`http://localhost:5000/users`, user).then(res => {
        	socket.emit('UserAdded', {'data' : {'user': user}})
   		})
	}
}
  • In the following code under client/src/components/UserList.js, function 'componentDidMount', socket.on consumes the emitted event 'userAddedResponse' by API and re-renders the component with the newly added user which was broadcasted within the event 'userAddedResponse.'
import { socket } from '../App'

export default class UserList extends React.Component {
  state = {
    users: []
  }

  componentDidMount() {
    socket.on('userAddedResponse', (resp) => {
      const users = this.state.users;
      const userAdded = resp.data.data.user;
      users.push(userAdded)
      
      this.setState({ users })
    });

    axios.get(`http://localhost:5000/users`)
      .then(res => {
        const users = res.data.users;
        this.setState({ users });
      })
  }
} 

With that, we will now have a real-time application in which when a user is added, the react component re-renders and lists the newly added user. It will also re-render and list the newly added user for all the other open tabs/windows. Similar logic can now be applied towards the other operations which might include updating and deleting an existing user.

Resources

What we implemented above is enough to make the application in real-time. But there are a lot of different parameters and options you can play with.

Find further details on Flask-SocketIO and on socket-io-client.