Server API
Complete API reference for the WebSocket server.
createElysiaWS()
Create an Elysia WebSocket handler with type-safe event handling.
import { createElysiaWS } from '@mdrv/wsx/server'
const { server, handler } = createElysiaWS(events, options?)
Parameters
events: EventDefinitions
Event schema defined with defineEvents(). See Event Definition Guide.
import { defineEvents } from '@mdrv/wsx/shared'
import { z } from 'zod'
const events = defineEvents({
ping: {
request: z.object({ timestamp: z.number() }),
response: z.object({ pong: z.string() }),
},
})
options?: ServerOptions
Optional configuration object:
interface ServerOptions {
validate?: boolean // Enable Zod validation (default: true)
debug?: boolean // Enable debug logging (default: false)
serializer?: Serializer // Custom serializer (default: CBOR)
}
Returns
Returns an object with two properties:
{
server: TypedServer, // Event handler registration
handler: ElysiaWSHandler // Elysia WebSocket handler
}
Event Handler Registration
server.onRequest(event, handler)
Handle request/response events.
server.onRequest('getData', async (payload, connection) => {
// Process request
const data = await fetchData(payload.id)
// Return response
return { name: data.name, age: data.age }
})
Parameters:
event: string- Event name (must haveresponsein schema)handler: (payload: T, connection: Connection) => Promise<R> | R- Request handler
Handler Parameters:
payload: T- Request payload (validated if validation enabled)connection: Connection- Connection object (see below)
Handler Returns: Response data matching the event’s response schema
Type Safety:
const events = defineEvents({
getData: {
request: z.object({ id: z.string() }),
response: z.object({ name: z.string(), age: z.number() }),
},
})
// ✅ TypeScript knows payload and response types
server.onRequest('getData', async (payload) => {
console.log(payload.id) // payload.id is string
return { name: 'Alice', age: 30 } // Must match response schema
})
// ❌ TypeScript error: wrong return type
server.onRequest('getData', async (payload) => {
return { wrong: 'field' }
})
Error Handling:
server.onRequest('getData', async (payload) => {
if (!payload.id) {
throw new Error('Invalid ID', { cause: { code: 'INVALID_ID' } })
}
const data = await fetchData(payload.id)
if (!data) {
throw new Error('Not found', { cause: { code: 'NOT_FOUND' } })
}
return data
})
Errors are automatically sent to the client as error responses.
server.onSend(event, handler)
Handle one-way messages (no response expected).
server.onSend('notify', async (payload, connection) => {
console.log('Notification:', payload.message)
// No return value
})
Parameters:
event: string- Event name (must NOT haveresponsein schema)handler: (payload: T, connection: Connection) => Promise<void> | void- Message handler
Connection Object
The connection object is passed to all event handlers:
interface Connection {
ws: ElysiaWebSocket // Raw Elysia WebSocket
send(event, payload): void // Send one-way message to client
respond(event, id, timestamp, result): void // Send response (internal use)
error(event, id, timestamp, error): void // Send error (internal use)
}
connection.send(event, payload)
Send a one-way message to the client.
server.onRequest('subscribe', async (payload, connection) => {
// Subscribe to updates
const interval = setInterval(() => {
connection.send('update', {
timestamp: Date.now()
})
}, 1000)
return { subscribed: true }
})
connection.ws
Access the raw Elysia WebSocket for advanced use cases.
server.onSend('notify', async (payload, connection) => {
// Access raw WebSocket
console.log('Client IP:', connection.ws.remoteAddress)
})
Elysia Integration
The handler object contains Elysia WebSocket lifecycle methods:
const { server, handler } = createElysiaWS(events)
new Elysia()
.ws('/ws', handler)
.listen(3000)
The handler object has these properties:
interface ElysiaWSHandler {
open: (ws: ElysiaWebSocket) => void
message: (ws: ElysiaWebSocket, message: ArrayBuffer | string) => void
close: (ws: ElysiaWebSocket) => void
}
These are automatically called by Elysia - you don’t need to call them directly.
Broadcasting
To broadcast messages to multiple clients, maintain a list of connections:
const connections = new Set<Connection>()
const { server, handler } = createElysiaWS(events)
// Store connections when clients connect
const originalOpen = handler.open
handler.open = (ws) => {
originalOpen(ws)
// Access connection from ws metadata if needed
}
server.onRequest('join', async (payload, connection) => {
connections.add(connection)
return { joined: true }
})
server.onRequest('broadcast', async (payload, connection) => {
// Send to all connected clients
for (const conn of connections) {
conn.send('message', { text: payload.message })
}
return { sent: connections.size }
})
Complete Example
import { createElysiaWS } from '@mdrv/wsx/server'
import { defineEvents } from '@mdrv/wsx/shared'
import { Elysia } from 'elysia'
import { z } from 'zod'
// Define events
const events = defineEvents({
// Request/response
ping: {
request: z.object({ timestamp: z.number() }),
response: z.object({ pong: z.string() }),
},
getData: {
request: z.object({ id: z.string() }),
response: z.object({ name: z.string(), age: z.number() }),
},
// One-way messages
notify: {
request: z.object({ message: z.string() }),
},
// Server -> Client
serverMessage: {
request: z.object({ text: z.string() }),
},
})
// Create server
const { server, handler } = createElysiaWS(events, {
validate: true,
debug: true,
})
// Handle requests
server.onRequest('ping', async (payload) => {
console.log('Ping at:', payload.timestamp)
return { pong: 'Hello from server!' }
})
server.onRequest('getData', async (payload, connection) => {
// Validate and fetch data
if (!payload.id) {
throw new Error('ID required')
}
const data = await database.get(payload.id)
if (!data) {
throw new Error('Not found', {
cause: { code: 'NOT_FOUND' }
})
}
return { name: data.name, age: data.age }
})
// Handle one-way messages
server.onSend('notify', async (payload, connection) => {
console.log('Client notification:', payload.message)
// Send message back to client
connection.send('serverMessage', {
text: `Received: ${payload.message}`
})
})
// Attach to Elysia
new Elysia()
.ws('/ws', handler)
.listen(3000)
console.log('WebSocket server running on ws://localhost:3000/ws')