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

The server requires @wooksjs/event-http because WebSocket connections start as HTTP upgrade requests. 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

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

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

The only difference is passing the ws package as _WebSocket. Everything else works the same.

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.

Next Steps

Released under the MIT License.