Written by: Yevhen Kozachenko (ekwoster.dev) on

From Zero to Serverless: Building a Real-Time Chat App with Deno, WebSockets, and No Backend

From Zero to Serverless: Building a Real-Time Chat App with Deno, WebSockets, and No Backend

Cover image for From Zero to Serverless: Building a Real-Time Chat App with Deno, WebSockets, and No Backend

From Zero to Serverless: Building a Real-Time Chat App with Deno, WebSockets, and No Backend

Imagine building a real-time chat application where there's no traditional backend—no Express, no database, not even a VM running 24/7. Sounds crazy? Welcome to the world of Deno and serverless WebSockets.

In this article, we’re diving deep into a lesser-known yet powerful use case: spinning up a real-time chat system using Deno Deploy, native WebSockets, and entirely stateless architecture. This is not your normal CRUD + PostgreSQL tutorial—this is an architectural eye-opener for modern developers.


Why This Setup Matters

Most chat apps are built on socket.io with Express or Nest.js, tethered to a server and a database with sessions, user tokens, etc. But what if:

  • You didn’t need an always-on server?
  • Your app scaled instantly under traffic?
  • You never paid for idle?
  • You didn’t even need a database for transient messages?

You can use Deno Deploy + WebSockets + Edge broadcast channels to build a chat app that’s instantly scalable and runs at the edge. Welcome to the serverless revolution.


Tech Stack

  • Deno Deploy - Serverless JavaScript/TypeScript runtime.
  • Native WebSockets - Real-time communication.
  • BroadcastChannel API - Native API to synchronise between edge locations (yes, stateless side channel!)
  • Vanilla HTML/JS frontend

What We'll Build

A real-time chat app:

  • Send/receive messages instantly
  • No backend
  • Client-side rendered
  • Completely stateless (no DB!)

Quick Preview

chat-demo


Getting Started

1. Project Structure

chat-app/
├── main.ts # Deno serverless handler
└── static/
    └── index.html # UI

The Magic in main.ts

// main.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts";

// Broadcast channel to relay messages across edge instances
const channel = new BroadcastChannel("global-chat");

serve((req) => {
  const { socket, response } = Deno.upgradeWebSocket(req);

  socket.onopen = () => {
    console.log("Client connected");
  };

  socket.onmessage = (event) => {
    const msg = event.data;
    // Broadcast to all edges / users
    channel.postMessage(msg);
  };

  // Relay messages to connected clients
  channel.onmessage = (e) => {
    socket.send(e.data);
  };

  socket.onclose = () => console.log("Client disconnected");

  return response;
});

Deploy it Now

You can instantly deploy this with:

deno deploy --project=chat-app main.ts

Frontend (index.html)

<!-- static/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Deno Chat</title>
  <style>
    body { font-family: sans-serif; margin: 1rem; }
    #messages { height: 300px; overflow-y: scroll; border: 1px solid #ccc; padding: 1rem; }
    input { width: 80%; }
  </style>
</head>
<body>
  <h2>💬 Deno Real-Time Chat</h2>
  <div id="messages"></div>
  <input id="messageInput" placeholder="Type a message..." />
  <button onclick="sendMessage()">Send</button>

  <script>
    const ws = new WebSocket("wss://your-deno-project.deno.dev");
    const messagesDiv = document.getElementById("messages");

    ws.onmessage = (event) => {
      const el = document.createElement("div");
      el.textContent = event.data;
      messagesDiv.appendChild(el);
      messagesDiv.scrollTop = messagesDiv.scrollHeight;
    };

    window.sendMessage = () => {
      const input = document.getElementById("messageInput");
      if (input.value) ws.send(input.value);
      input.value = "";
    };
  </script>
</body>
</html>

Curious Edge Tech: The Broadcast Channel

Deno Deploy has support for BroadcastChannel, which is essentially a real-time communication tunnel across Deno's global edge locations.

This is the key. Since your WebSocket connections might hit different edge servers, a local in-memory relay won’t work. But with BroadcastChannel, messages get broadcast to all connected edge nodes, creating true global state synchronization—without Redis or databases.


Real-Life Use Cases

  • Live score update apps
  • Real-time dashboards
  • Multiplayer browser games
  • Temporary real-time community chats (like Reddit AMAs)

What’s Missing (and Why That’s Okay)

This app is great for use cases where message history is ephemeral. Think more WhatsApp group live than Slack workspace. If you need persistence:

  • Add Supabase/PlanetScale/Fauna for DB storage.
  • Store messages on the client with IndexedDB.

But if your messages can vanish after reload, you just built a beautiful stateless chat system, instantly scalable, with no backend code beyond a single file. Pretty cool, huh?


Next Steps

  • Deploy this app on https://deno.com/deploy
  • Add localStorage to cache messages client-side
  • Add usernames, emojis, and typing indicators
  • Add authentication using Deno KV (Redis-like storage)

Lessons Learned

✅ WebSockets are amazing — and native everywhere now
✅ Deno Deploy lets you build at the edge without thinking about servers
✅ Broadcast Channel turns stateless services into networked ones
✅ Simplicity wins — one TypeScript file > 4 services


Resources

  • Deno Deploy
  • BroadcastChannel API
  • Deno WebSocket Docs

If you're still binding socket.io to Express in 2024, you're working too hard. Embrace the edge. 🧠


Found this cool or have questions? Email me or drop a DM on Twitter @edgechad


💡 If you need this done – we offer fullstack development services! Check it out here