Building and Deploying a Real-Time WebSocket Chat Application with Deno ๐ฆ, Oak ๐ฟ๏ธ, and Render ๐

Have you ever wanted to build a real-time chat application but felt overwhelmed by the technical details? Or maybe youโve wondered how to deploy your app without emptying your wallet?
Well, youโre in the right place! Imagine creating a WebSocket chat app from scratch, using the power of Deno ๐ฆ and Oak ๐ฟ๏ธ, and then deploying it for free with Render ๐. Sounds pretty awesome, right?
In this article, Iโm going to show you how to do just that. Weโll walk through setting up the Socket Deno ๐๐ฆ template, running it locally, and deploying it so you can see your app live on the web โ all without spending a cent.
Why Deno and Oak? ๐ฆ๐ฟ๏ธ
Honestly speaking, I wanted to try something new, and you should too! I stumbled upon Bun, Hono, and Cloudflare Workers in my journey, but they were a bit tricky for me to get a handle on.
Thatโs when I discovered the Deno + Oak combo. Deno is a modern, secure runtime for JavaScript and TypeScript thatโs super straightforward. Oak is a lightweight framework that makes building web apps a breeze. Pairing them up and deploying on Render? Itโs been a game-changer for me.
Letโs dive into why this stack is so awesome and how it can help you build your own real-time chat app with ease.
Check Out the Repo and Set Up Locally ๐๏ธ
Want to get your hands on the code? Head over to my GitHub repository for the Socket Deno ๐๐ฆ template. The repo has everything you need to get started, including detailed setup instructions.
Understanding the Code ๐
WebSocket Setup ๐ ๏ธ
connectedClients
: Keeps track of all active WebSocket connections with usernames as keys.broadcast(message)
: Sends a message to all connected clients.broadcast_usernames()
: Updates all clients with the current list of usernames.
Handling WebSocket Connections ๐
/start_web_socket
Route: Upgrades the connection to WebSocket, manages client connections, and handles events like sending and receiving messages.
Connection Events
onopen
: Updates all clients with new usernames when someone connects.onclose
: Removes clients and updates usernames when someone disconnects.onmessage
: Processes incoming messages and broadcasts them.
Serving Static Files ๐
- Serves
index.html
from thepublic
directory for the root URL.
Starting the Server ๐
- Listens on port
8080
and logs the URL.
Deploying on Render ๐
Time to go live! Hereโs how to deploy your WebSocket chat app on Render:
Push Your Code to GitHub ๐งโ๐ป
- Make sure your code is pushed to a GitHub repo. If you havenโt done that yet, go ahead and upload it.
Set Up on Render ๐
- Log in to Render and connect it to your GitHub account.
- Follow the deployment instructions in the README file of my GitHub repo.
And thatโs it! Render will handle the rest and provide you with a URL to your live app. Easy peasy!
Full Code โจ๏ธ
Server.js
import { Application, Router } from "@oak/oak";
const connectedClients = new Map();
const app = new Application();
const port = 8080;
const router = new Router();
function broadcast(message) {
for (const client of connectedClients.values()) {
try {
client.send(message);
} catch (error) {
console.error("Error broadcasting message:", error);
}
}
}
function broadcast_usernames() {
const usernames = [...connectedClients.keys()];
console.log(
"Sending updated username list to all clients: " + JSON.stringify(usernames)
);
broadcast(
JSON.stringify({
event: "update-users",
usernames: usernames,
})
);
}
router.get("/start_web_socket", async (ctx) => {
const socket = await ctx.upgrade();
const username = ctx.request.url.searchParams.get("username");
if (connectedClients.has(username)) {
socket.close(1008, `Username ${username} is already taken`);
return;
}
socket.username = username;
connectedClients.set(username, socket);
console.log(`New client connected: ${username}`);
socket.onopen = () => {
broadcast_usernames();
};
socket.onclose = () => {
console.log(`Client ${socket.username} disconnected`);
connectedClients.delete(socket.username);
broadcast_usernames();
};
socket.onmessage = async (m) => {
try {
const data = JSON.parse(m.data);
switch (data.event) {
case "send-message":
await broadcast(
JSON.stringify({
event: "send-message",
username: socket.username,
message: data.message,
})
);
break;
default:
console.warn("Unknown event:", data.event);
}
} catch (error) {
console.error("Error handling message:", error);
}
};
socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
});
app.use(router.routes());
app.use(router.allowedMethods());
app.use(async (context) => {
await context.send({
root: `${Deno.cwd()}/`,
index: "public/index.html",
});
});
console.log("Listening at http://localhost:" + port);
await app.listen({ port });
Contributing and License ๐ค
Want to contribute? Great! If you spot any bugs or have ideas for improvements, feel free to:
- Submit an Issue: Report any problems or feature requests on the GitHub Issues page.
- Send a Pull Request: Got a fix or new feature? Submit a pull request with your changes, and Iโll review and merge them.
- License ๐: This project is licensed under the MIT License.
Wrap-Up and Thanks! ๐
Thatโs a wrap โ your WebSocket chat app is live! ๐ If you enjoyed this guide, hit the โญ๏ธ on my GitHub repo to show some love & support.
Feel free to tweak, experiment, and have fun with the code. If you need help or just want to chat, drop a comment or reach out.
Happy coding and see you around! ๐๐ฆ