WebSockets

A pub/sub WebSockets implementation based on Cloudflare Durable Objects, behind the websockets feature flag.

Real-time updates run on Cloudflare Workers and Durable Objects, behind the websockets flag in config.ts.

Architecture

  • Frontend WebSocket client: apps/app/app/plugins/websockets.client.ts (a companion plugin, ws-notifications.client.ts, wires this into the notifications feature)
  • Backend WebSocket server: apps/functions/src/websockets.js
  • Infrastructure: infra/websockets.ts

Usage

Frontend

The WebSocket client is a Nuxt plugin, available in components via $ws:

// In your component
const { $ws } = useNuxtApp();

// Subscribe to topics
$ws.on("user.123", (data) => {
  // Handle topic payload
  console.log("Got update:", data);
});

// Cleanup when done
onUnmounted(() => {
  $ws.off("user.123");
});

The client automatically:

  • Authenticates using a short-lived WebSocket ticket (/api/auth/ws-ticket) rather than the long-lived session token — the sec-websocket-protocol header is the only way to authenticate a browser WebSocket connection, and it's readable by page JS, so the real access token never goes there.
  • Reconnects on connection drops.
  • Resubscribes to topics after reconnection.

Backend

Publish messages to topics from a tRPC procedure via ctx.sendWebsocketsEvent:

async ({ ctx: { user, sendWebsocketsEvent }, input }) => {
  // ...create notification in database...
  await sendWebsocketsEvent(
    user.id, // topic
    { type: "notification", payload: notification },
  );
};

Security

  • Authentication uses a short-lived ticket, signed with a secret shared only between the App Worker and the Websockets Worker (WsTicketSecret, infra/websockets.ts) — never the OpenAuth session token.
  • Topic authorization is defined by whatever the app chooses to publish to a given topic — there's no built-in per-topic ACL, so scope topic names (e.g. user.<id>) to whoever should legitimately receive them.

Configuration

Enable WebSockets by setting the websockets flag:

config.ts
export const flags = {
  // ...
  websockets: true,
};

This deploys the WebSocket Worker (backed by a Durable Object) and links it into the app.

Nuxfire Production Kit

Ready to build and launch your SaaS?

Get 100% full source code ownership, zero proprietary wrappers, and architecture engineered for millions of requests on Cloudflare.

© 2026 Nuxfire