How to log data in real-time on a web page using Flask-SocketIO in Python

Fundamentals of web sockets explored in 7 mins

XQ
XQ
Dec 17, 2021 · 7 min read
Photo by Luke Chesser on Unsplash

Communication between clients and servers is part and parcel of the internet. Web sockets are a protocol developed to facilitate the same. They enable us to do two-way contact between a client (or multiple clients) and a server. You can think of your web browser as a client, for example. The server can be a backend program running on your system.

Given this context, web sockets are very integral to applications that run on the internet. However, I haven’t personally used much of them. I wanted to learn more and here is a documentation of my initial exploration.

Here’s what I want to do:

➡️ Let’s say you have a source of data that keeps changing with time.
➡️ You want to show that updating data in a web application.

A web socket can be used to broadcast such data to all the clients. I will be exploring an approach in Python using Flask and a library called Flask-SocketIO that helps us use WebSockets in a Flask app.

What data should I display? There are some interesting possibilities. For example;

  • Real-time logs.
  • Tweets from a particular hashtag.
  • Financial data.
  • Cryptocurrency prices.

I am going with the last one as it is quite straightforward to get the data. Coinbase has a public API that we can use to fetch the real-time prices of cryptocurrencies. I am going to write a flask application to log the current bitcoin price in USD.

Before we start the coding part, let us logically understand what we need to do.

  1. Create a Flask server application to run on our localhost.
  2. Get the current price of bitcoin using the Coinbase API endpoints every second (or a couple of seconds).
  3. Create a broadcast message with this data at the server-side and send it to all clients who access this server in real-time. Our clients in this context are the local hosts.

Creating a simple flask app is extremely easy. Here’s a code sample from a blog I referred to during my exploration:

# Basic Flask Python Web App
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"

if __name__ == "__main__":
app.run()

That’s it. Create a python file, copy-paste this code, and run it. Visit localhost:5000 on your web browser and you will see "Hello World". This simple code sample helps us send some data from our code to the web browser. That’s our starting point.

Note: you need to have the required libraries installed before you run this. You can do that via the pip command in your console. e.g. pip install flask

Using the Coinbase API to get the bitcoin price is also extremely simple because it's a public API without a need for an API key or authentication.

import requests# Coinbase API endpoint
url = 'https://api.coinbase.com/v2/prices/btc-usd/spot'
price = ((requests.get(url)).json())['data']['amount']
print(price)

Run the above code in python and it will print the current price of bitcoin in USD. If you want to dive deeper into Coinbase APIs, you can refer to their official documentation.

Next, we need to see how we can use Flask-SocketIO. As with any framework, the best place to start is the official documentation and examples. So, I went to the Github repo of this library and took the example code to tinker on top of it. All the boilerplate code you need is already here.

If you want a deeper understanding, you can read the below blog. For this tutorial, you do not need all the info though.

I removed everything from the example code except the stuff that I found relevant to my use case.

Here’s the code for the flask app (app.py). Let us try to understand it block by block.

One key change I made was to include the Coinbase API GET call to fetch bitcoin price.

price = ((requests.get(url)).json())['data']['amount']

To understand more about the requests library and how to use it, you can refer to the official documentation.

Also, you may notice the use of lock() in the code. To know more about it, you can refer to the below blog. For this tutorial, you do not need the full context. You can come back to refer to this later.

Here are some other key blocks you may notice in the code.

@app.route('/')
def index():
return render_template('index.html', async_mode=socketio.async_mode)

Here, we are setting the app routes. The default ‘/’ corresponds to the index or the first page that loads when you visit localhost. render_template will return the HTML file we created to be rendered on the webpage. index.html must be put in the templates folder for it to be automatically picked up and read in the project. The contents of my index.html file are here;

The most important part to notice is the event handler. Do note that we can create a new client app with any other framework too. Perhaps, something to explore in a future blog.

socketio.event or @socketio.on('name_of_event') is what we use to define an event, which is some communication or function we want to do. send() and emit() functions can be used to communicate data to the client. The difference being send() is used for unnamed events and emit() is used for named events. Setting broadcast = true in these functions will send the details to all clients connected to this server.

To practically summarize:

  • The client can trigger the 'test_message' (or any such event) via some user action (like clicking on a button). This can be added in the <script> part of index.html using the socket.emit() function. The data object sent is received by the server.
$('form#test').submit(function(event) {
socket.emit('test_message', {data: 'Test message sent'});
return false;
});
  • The corresponding button can be rendered in the <body> which the user can click to trigger this 'test_message' event. id parameter is used as a reference to map the button to the event handler.
<form id="test" method="POST" action="#">
<input type="submit" value="Test">
</form>
  • You can create this same event in your app.py server so that once triggered, you can send a response back to the client. Here’s how it looks like;
#Receive a request from client and send back a test response
@socketio.on('test_message')
def handle_message(data):
print('received: ' + str(data))
emit('test_response', {'data': 'Test response sent'})
  • This response is received by the client which can then use the same to render on the UI. Here, we are simply creating a new div element to render which is appended to the element with id = "log”.
//Test response received from server is handled here
socket.on('test_response', function(msg) {
$('#log').append('<br>' + $('<div/>').text(msg.data).html());
})

I have added the same flow for a broadcast message too. Using a similar flow logic we can directly generate server-side events and broadcast them to all the clients. That’s what we are doing with the bitcoin price data using the background_thread function we defined.

In short, you need three code blocks in your client-server interaction:

  1. A client-side function that can trigger an event. (This is not needed if the event is directly triggered by the server)
  2. A server-side code that defines this event and once triggered, can perform an action or send a response to the client.
  3. A client-side code to render the data that it receives from the server (or do something with it).

All of this can be done using the various functions and features provided by web sockets.

Demo

In our code, we have just two files. The server-side app.py and the client-side index.html put in the /templates folder (this is a file hierarchy required by Flask).

If you run your project with python app.py , you will get the following result in your localhost. The current bitcoin price is fetched and displayed automatically every 3 seconds (as we set it).

You can see two buttons we have put for test and broadcast. Clicking on Test will trigger the 'test_message' event that we discussed before. The response sent by the server to the client is rendered in the receiving part of the UI.

The broadcast button does the same thing but it sends the message to all the clients. Note that the bitcoin price is also sent to all the clients. To test the same, you can open multiple local hosts in multiple browsers and click on the broadcast button. The message will be sent to all the local hosts/browsers and is displayed. In the case of the Test button, you will notice that the response message is only displayed in that browser tab where you clicked it.

Using the same logical flow, you can display any other data, say logs, or tweets, or weather. You can also modify this client-side to show better UI. For example, how about fetching cryptocurrency prices in real-time and plotting a graph instead of just showing the value. Your imagination is the limit with what you want to do with the data or how you want to show it.

Here’s the Github repo of the code for you to clone and tinker with:

Note: In case you notice any errata in my understanding, feel free to reach out and let me know of the same and I will update the blog post accordingly.

The Research Nest

Empowering humanity with exclusive insights.