WebSocket

SpaceNode includes a built-in WebSocket server (RFC 6455) with zero external dependencies. Supports fragment assembly, ping/pong heartbeat, and backpressure.

Basic Usage

import { createApp } from 'SpaceNode'

const app = await createApp()

app.ws('/chat', (socket, req, services) => {
  console.log('Client connected')

  socket.on('message', (msg) => {
    console.log('Received:', msg)
    socket.send(`Echo: ${msg}`)
  })

  socket.on('close', () => {
    console.log('Client disconnected')
  })
})

app.listen(3000)
DI in WebSocket — The third argument services gives you access to all registered services, same as in HTTP controllers.

Socket API

Method / PropertyDescription
socket.send(data)Send text (string) or binary (Buffer). Returns false on backpressure
socket.close(code?)Close connection (default code 1000)
socket.pause()Pause receiving data (for backpressure from consumer side)
socket.resume()Resume receiving data after pause
socket.on('message', fn)Receive messages (string for text, Buffer for binary)
socket.on('close', fn)Connection closed
socket.on('error', fn)Connection error
socket.on('ping', fn)Ping frame received
socket.on('pong', fn)Pong frame received
socket.readyState0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED

Backpressure

socket.send() returns a boolean — false means the socket buffer is full. Use pause() / resume() to manage flow control:

const ok = socket.send('hello')
if (!ok) {
  socket.pause()  // pause reading until drain
}

Origin Validation

Restrict which origins can establish WebSocket connections:

const app = await createApp({
  wsOrigins: ['https://mysite.com', 'https://admin.mysite.com'],
})

Fragment Assembly

Large messages may arrive as multiple WebSocket frames (fragments). SpaceNode automatically reassembles fragments into complete messages before emitting the 'message' event.

Ping/Pong Heartbeat

SpaceNode automatically sends ping frames every 30 seconds to keep connections alive. Clients that don't respond with a pong within 60 seconds are disconnected.

Multiple Endpoints

app
  .ws('/chat', chatHandler)
  .ws('/notifications', notifyHandler)
  .ws('/live-data', dataHandler)

Broadcasting

const clients = new Set()

app.ws('/chat', (socket) => {
  clients.add(socket)

  socket.on('message', (msg) => {
    // Broadcast to all connected clients
    for (const client of clients) {
      if (client !== socket && client.readyState === 1) {
        client.send(msg)
      }
    }
  })

  socket.on('close', () => clients.delete(socket))
})

Client-Side

// Browser JavaScript
const ws = new WebSocket('ws://localhost:3000/chat')

ws.onopen = () => ws.send('Hello!')
ws.onmessage = (e) => console.log('Server says:', e.data)
Implementation — Uses raw node:http upgrade event + node:crypto for the WebSocket handshake. Supports text and binary frames, fragment assembly, ping/pong heartbeat (30s/60s), backpressure detection, origin validation, and close frames per RFC 6455. Zero external dependencies.