circuit

WebSockets In Python FastAPI — Fetching Data At Super Speed


Meet the faster brother of HTTP requests — WebSockets. I'm going to assume you kinda know what HTTP requests are, but in case you didn't, here's a brief explanation.

HTTP Requests In Python FastAPI

# app.py
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/test")
def test():
    return {"message": "hello world"}if __name__ == "__main__":
    uvicorn.run("app:app")

^ A simple FastAPI application with 1 endpoint /test. After we run app.py, we can make an HTTP GET request by typing http://localhost:8000/test in our browser. We will receive a JSON response {"message": "hello world"}

However, if we want our frontend to fetch a fast-changing number multiple times per second from our backend, HTTP doesn't seem like such a great idea — we would need to make multiple HTTP requests per second. This is where I recommend using WebSockets in place of HTTP requests.

WebSockets VS HTTP Requests

  1. WebSockets are much faster
  2. Bi-directional protocol — both client and server can tell each other to do stuff in real-time

Summary — WebSockets are the better choice if our frontend component needs to be updated very frequently (many times per second).

WebSockets In Python FastAPI

# app.py
import uvicorn
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/test")
async def test(websocket: WebSocket):
    await websocket.accept()
    while True:
        request = await websocket.receive_text()
        print(request)
        for i in range(10000):
            await websocket.send_text(str(i+1))
if __name__ == "__main__":
    uvicorn.run("app:app")

^ A simple FastAPI application with a WebSocket endpoint /test. Here, once the frontend makes a call to our WebSocket endpoint, our WebSocket will quickly send all numbers from 1 to 10000 to our frontend. Some HTML code for you to test this:

// test.html
<script>
    var ws = new WebSocket("ws://localhost:8000/test")
    ws.onmessage = event => {
        var number = document.getElementById("number")
        number.innerHTML = event.data
    }
    handleOnClick = () => {
        ws.send("hi")
    }
</script>
<button onclick="handleOnClick()">Click Me</button>
<div id="number">0</div>

If we click on the button, notice that the number rapidly increases from 0 to 10000. This would be computationally crazy if we had done this using HTTP get requests!

WebSockets In Python FastAPI — Dealing With JSON data

Often times we want to deal with JSON data instead of text data when making our frontend and backend applications communicate. Here's how we can do this in our backend:

import uvicorn
from fastapi import FastAPI, WebSocketapp = FastAPI()
@app.websocket("/test")
async def test(websocket: WebSocket):
    await websocket.accept()
    while True:
        request = await websocket.receive_json()
        message = request["message"]
        for i in range(10000):
            await websocket.send_json({
                "message": f"{message} - {i+1}",
                "number": i+1
            })
if __name__ == "__main__":
    uvicorn.run("app:app")

Notice that 1) we replaced the receive_text method with receive_json and 2) we replaced the send_text method with the send_json method.

And the HTML file:

<script>
    var ws = new WebSocket("ws://localhost:8000/test")
    ws.onmessage = event => {
        var number = document.getElementById("number")
        var message  = document.getElementById("message")
        data = JSON.parse(event.data)
number.innerHTML = data.number
        message.innerHTML = data.message
    }
handleOnClick = () => {
        ws.send(JSON.stringify({message: "hello"}))
    }
</script>
<button onclick="handleOnClick()">Click Me</button>
<div id="message">hi</div>
<div id="number">0</div>

Notice that we use JSON.stringify to convert an object into a string, and JSON.parse to convert a string into an object.




Continue Learning