WebSockets
DocsNetworkingWebSockets

WebSockets

WebSockets work on StackBlaze web apps. Connect to the app’s platform hostname (or a custom domain you attached).

The app's public HTTPS URL accepts wss:// connections, a wscat session in the terminal exchanges ping and pong, and connection state moves to Valkey when the service scales out.

Connect to the platform hostname

Use wss:// against the app’s *.stackblaze.app URL — for example wss://my-service.stackblaze.app/ws. Copy the exact host from the app if yours includes a region segment. Custom domains work the same way over wss:// after DNS and TLS are in place.

Your process should accept the WebSocket upgrade on the port from PORT. Server-Sent Events work on the same public URL.

Libraries

Common WebSocket libraries work without a StackBlaze-specific adapter:

ws (Node.js)

server.ts
import { WebSocketServer } from 'ws'
import { createServer } from 'http'

const server = createServer(app)
const wss = new WebSocketServer({ server })

wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    ws.send(data.toString())
  })

  const interval = setInterval(() => ws.ping(), 30_000)
  ws.on('close', () => clearInterval(interval))
})

server.listen(process.env.PORT || 8080)

Socket.IO

server.ts
import { createServer } from 'http'
import { Server } from 'socket.io'

const httpServer = createServer(app)
const io = new Server(httpServer, {
  cors: {
    origin: process.env.ALLOWED_ORIGINS?.split(',') ?? '*',
    methods: ['GET', 'POST'],
  },
  transports: ['websocket', 'polling'],
})

io.on('connection', (socket) => {
  socket.on('join-room', (roomId) => {
    socket.join(roomId)
  })
})

httpServer.listen(process.env.PORT || 8080)

Server-Sent Events (SSE)

server.ts
app.get('/events', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    'X-Accel-Buffering': 'no',
  })

  const send = (data: object) => {
    res.write(`data: ${JSON.stringify(data)}\n\n`)
  }

  send({ type: 'connected' })

  const interval = setInterval(() => {
    send({ type: 'heartbeat', timestamp: Date.now() })
  }, 15_000)

  req.on('close', () => {
    clearInterval(interval)
  })
})

Keep-alives

Send a ping or heartbeat from the client or server so idle connections stay open. Example client:
Client-side keep-alive
const ws = new WebSocket('wss://my-service.stackblaze.app/ws')

const ping = setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }))
  }
}, 30_000)

ws.addEventListener('close', () => clearInterval(ping))

More than one replica

A WebSocket stays on the replica that accepted it. To broadcast to every connected client, use a shared pub/sub (for example Redis attached in the same phase) so each replica can fan out to its local sockets.

Socket.IO Redis adapter

server.ts
import { createAdapter } from '@socket.io/redis-adapter'
import { createClient } from 'redis'

const pubClient = createClient({ url: process.env.REDIS_URL })
const subClient = pubClient.duplicate()

await Promise.all([pubClient.connect(), subClient.connect()])

io.adapter(createAdapter(pubClient, subClient))
io.emit('announcement', { message: 'Server update in 5 minutes' })