Skip to content

Get Started with WebSocket

Experimental

This package is in an experimental phase. The API may change without following semver until it reaches a stable release.

INFO

Learn more about Wooks to understand its philosophy and advantages:

Installation

The WebSocket flavor uses two packages — one for the server, one for the client:

bash
# Server
npm install @wooksjs/event-ws @wooksjs/event-http ws

# Client (browser or Node.js)
npm install @wooksjs/ws-client

@wooksjs/event-http is needed when you serve WebSocket alongside HTTP routes (integrated mode). For a standalone WebSocket server, only @wooksjs/event-ws and ws are required (see Standalone Server below). The ws package provides the underlying WebSocket implementation for Node.js.

AI Agent Skills

Wooks provides a unified skill for AI coding agents (Claude Code, Cursor, Windsurf, Codex, etc.) that covers all packages with progressive-disclosure reference docs.

bash
npx skills add wooksjs/wooksjs

Learn more about AI agent skills at skills.sh.

Server: Hello World

A minimal server that echoes messages back to the sender.

ts
import { createHttpApp } from '@wooksjs/event-http'
import { createWsApp, useWsMessage, useWsConnection } from '@wooksjs/event-ws'

const http = createHttpApp()
const ws = createWsApp(http) // auto-registers as upgrade handler

// HTTP route that upgrades to WebSocket
http.upgrade('/ws', () => ws.upgrade())

// Handle "echo" events on any path
ws.onMessage('echo', '/*', () => {
  const { data, path } = useWsMessage()
  return { echoed: data, path } // → sent back as reply
})

http.listen(3000, () => {
  console.log('WebSocket server ready on ws://localhost:3000/ws')
})

Messages are routed by event type (like an HTTP method) and path (like a URL). The handler return value is sent back as a reply when the client used call() (RPC).

Client: Connecting

Browser

ts
import { createWsClient } from '@wooksjs/ws-client'

const client = createWsClient('ws://localhost:3000/ws', {
  reconnect: true,
  rpcTimeout: 5000,
})

client.onOpen(() => console.log('Connected!'))

// RPC — returns a Promise
const result = await client.call('echo', '/hello', { message: 'Hi!' })
console.log(result) // → { echoed: { message: 'Hi!' }, path: '/hello' }

Node.js

Node.js 22+ has a native global WebSocket — the browser example above works as-is, no extra setup. On Node.js < 22, install the ws package and expose it as a global polyfill before creating the client:

ts
import WebSocket from 'ws'
import { createWsClient } from '@wooksjs/ws-client'

globalThis.WebSocket ??= WebSocket as any

const client = createWsClient('ws://localhost:3000/ws', {
  rpcTimeout: 5000,
})

Adding Rooms

A chat server where clients join rooms and broadcast messages.

ts
import { createHttpApp } from '@wooksjs/event-http'
import {
  createWsApp,
  useWsMessage,
  useWsRooms,
  WsError,
} from '@wooksjs/event-ws'

const http = createHttpApp()
const ws = createWsApp(http)

http.upgrade('/ws', () => ws.upgrade())

// Join a room
ws.onMessage('join', '/chat/:room', () => {
  const { data } = useWsMessage<{ name: string }>()
  if (!data?.name) throw new WsError(400, 'Name is required')

  const { join, broadcast } = useWsRooms()
  join()  // joins the room matching the current path: /chat/:room
  broadcast('system', { text: `${data.name} joined` })
  return { joined: true }
})

// Send a message to a room
ws.onMessage('message', '/chat/:room', () => {
  const { data } = useWsMessage<{ text: string; from: string }>()
  const { broadcast } = useWsRooms()
  broadcast('message', data)  // sends to all room members except sender
})

http.listen(3000)
ts
// Client usage
const client = createWsClient('ws://localhost:3000/ws')

await client.call('join', '/chat/general', { name: 'Alice' })

client.on('message', '/chat/general', ({ data }) => {
  console.log(`${data.from}: ${data.text}`)
})

client.on('system', '/chat/general', ({ data }) => {
  console.log(`[system] ${data.text}`)
})

client.send('message', '/chat/general', { text: 'Hello!', from: 'Alice' })

Standalone Server

If you don't need HTTP routes alongside WebSocket, use standalone mode:

ts
import { createWsApp, useWsMessage } from '@wooksjs/event-ws'

const ws = createWsApp()

ws.onMessage('echo', '/*', () => {
  const { data } = useWsMessage()
  return data
})

ws.listen(3000)

In standalone mode, all connections are accepted — there's no HTTP upgrade route to configure.

Shutting down

Shut down with ws.close() — it stops the heartbeat and closes every connection with code 1001 (Server shutting down). Note that close() does not close the underlying HTTP server created by listen(); retrieve it with ws.getServer() and close it yourself:

ts
ws.close()
ws.getServer()?.close()

In HTTP-integrated mode, getServer() returns undefined — the HTTP server belongs to @wooksjs/event-http.

Next Steps

  • Introduction — Philosophy and an overview of the server and client packages.
  • Composables — Full reference for all server composables and server options.
  • Rooms & Broadcasting — Deep dive into room management and broadcasting.
  • Client Guide — Complete client API: RPC, subscriptions, reconnection, error handling.
  • Wire Protocol — Understand the JSON message format.
  • Testing — Run WS handlers in isolated test contexts.
  • Logging — Event-scoped logging in WS handlers.

Released under the MIT License.