# 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
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
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
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
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
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
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
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
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
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
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