# Workshop: WebSocket

WARNING

  • Please prepare one folder for this Workshop

# Start Coding (Dev environment)

# project structure

+-- backend
|    +-- src 
|    |   +-- main.py
|    +-- Dockerfile.dev
+-- frontend
|    +-- src 
|    |   +-- app.js
|    |   +-- index.html
|    +-- Dockerfile 
+-- docker-compose.yml
1
2
3
4
5
6
7
8
9
10

./docker-compose.yml










 
 









 
 
version: "3"
services: 

    frontend:
          build:
            context: ./frontend

          ports:
            - "80:80"
          volumes:
            - ./frontend/src:/usr/share/nginx/html


    backend:
          build:
            context: ./backend
            dockerfile: Dockerfile.dev

          ports:
            - "8000:8000"
          volumes:
            - ./backend/src:/home/src 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Backend

./backend/Dockerfile.dev








 
FROM python:3.9.7-buster
WORKDIR /home/src

RUN pip install "fastapi==0.68.1"
RUN pip install uvicorn[standard]

COPY ./src /home/src/
CMD uvicorn --host 0.0.0.0 main:app --forwarded-allow-ips '*' --reload 
1
2
3
4
5
6
7

./backend/src/main.py

from fastapi import FastAPI,WebSocket,WebSocketDisconnect

app = FastAPI()

base_path:str = "/"

clients:list[WebSocket] = []

@app.websocket(base_path)
async def chat(ws:WebSocket):
    try:
        await ws.accept()
        clients.append(ws)
        while True:
            text:str = await ws.receive_text()
            for client in clients:
                await client.send_text(text)

    except WebSocketDisconnect:
        clients.remove(ws)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Frontend

./frontend/Dockerfile

FROM nginx:stable-alpine

COPY ./src /usr/share/nginx/html

CMD ["nginx", "-g", "daemon off;"]
1
2
3
4

./frontend/src/app.js












 












let wsConnection;

let chatLogsDom = document.getElementById("chatLogs")

const insertLog = (data) => {
    let pTag = document.createElement("p")
    pTag.innerHTML = data
    chatLogsDom.prepend(pTag)
}

const connectWS = () => {
    wsConnection = new WebSocket("ws://localhost:8000")
    wsConnection.onopen = (ev) => {
        wsConnection.send("New user joined")
    }
    wsConnection.onmessage = (ev) => {
        insertLog(ev.data)
    }
}

let userTextDom = document.getElementById("userText")
const sendUserText = () => {
    wsConnection.send(userTextDom.value)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

./frontend/src/index.html

<!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>Document</title>
</head>

<body>
    <h1>Chat</h1>
    <button onclick="connectWS()">click here to connect</button>
    <h2>Chat logs</h2>
    <input type="text" id="userText">
    <button onclick="sendUserText()">send</button>
    <div id="chatLogs"></div>

    <script src="./app.js"></script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Run containers

docker-compose up --build -d
1

# Remove containers

docker-compose down
1

# Start Coding (Production Ready)

# project structure





 






 

+-- backend
|    +-- src 
|    |   +-- main.py
|    +-- Dockerfile.dev
|    +-- Dockerfile.prod
+-- frontend
|    +-- src 
|    |   +-- app.js
|    |   +-- index.html
|    +-- Dockerfile 
+-- docker-compose.yml
+-- docker-compose-prod.yml
1
2
3
4
5
6
7
8
9
10
11
12

./docker-compose-prod.yml

version: "3"
services: 

    frontend:
          build:
            context: ./frontend

          ports:
            - "80:80"
         
    backend:
          build:
            context: ./backend
            dockerfile: Dockerfile.prod

          ports:
            - "8000:8000"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

./backend/Dockerfile.prod








 
FROM python:3.9.7-buster
WORKDIR /home/src

RUN pip install "fastapi==0.68.1"
RUN pip install uvicorn[standard]

COPY ./src /home/src/
CMD uvicorn --host 0.0.0.0 main:app --forwarded-allow-ips '*'
1
2
3
4
5
6
7

# Run containers

docker-compose -f ./docker-compose-prod.yml up --build -d
1

# Remove containers

docker-compose -f ./docker-compose-prod.yml down
1

WARNING

As you can see in app.js, localhost is not working if you have a domain name (when you deploy it).

wsConnection = new WebSocket("ws://localhost:8000")
1

You can change it to this. location.hostname returns the current hostname.

wsConnection = new WebSocket(`ws://${location.hostname}:8000`)
1