Quick Start
Get up and running with @mdrv/wsx in 5 minutes.
Installation
bun add @mdrv/wsx zod cbor-x elysia
1. Define Your Events
Create a shared event schema that both client and server will use:
// events.ts
import { defineEvents } from '@mdrv/wsx/shared'
import { z } from 'zod'
export const events = defineEvents({
ping: {
request: z.object({ timestamp: z.number() }),
response: z.object({ pong: z.string() }),
},
notify: {
request: z.object({ message: z.string() }),
// No response field = one-way message
},
})
The defineEvents function creates a type-safe event schema where:
requestdefines the payload structure sent by the clientresponsedefines the structure returned by the server (optional for one-way messages)
2. Create the Server
// server.ts
import { createElysiaWS } from '@mdrv/wsx/server'
import { Elysia } from 'elysia'
import { events } from './events'
const { server, handler } = createElysiaWS(events, {
validate: true, // Enable Zod validation
debug: true, // Enable debug logging
})
// Handle request/response
server.onRequest('ping', async (payload) => {
console.log('Received ping:', payload.timestamp)
return { pong: 'Hello from server!' }
})
// Handle one-way messages
server.onSend('notify', async (payload) => {
console.log('Notification:', payload.message)
})
// Attach to Elysia
new Elysia()
.ws('/ws', handler)
.listen(3000)
console.log('WebSocket server running on http://localhost:3000/ws')
3. Create the Client
// client.ts
import { createClient } from '@mdrv/wsx/client'
import { events } from './events'
const client = createClient('ws://localhost:3000/ws', events, {
validate: true,
debug: true,
})
client.connect()
client.onOpen(async () => {
console.log('Connected to server!')
// Send request and await response
const result = await client.request('ping', {
timestamp: Date.now(),
})
console.log('Server responded:', result.pong)
// Send one-way message
client.send('notify', {
message: 'Hello from client!',
})
})
client.onClose(() => {
console.log('Disconnected from server')
})
client.onError((error) => {
console.error('Connection error:', error)
})
4. Run Your Application
# Terminal 1: Start the server
bun server.ts
# Terminal 2: Run the client
bun client.ts
You should see:
Connected to server!
Server responded: Hello from server!
What’s Next?
- API Reference - Explore the full API
- Guides - Learn about advanced features
- Examples - See working examples
- Migration - Migrate from older versions
Key Concepts
Type Safety
All events are fully typed. TypeScript will autocomplete event names and payloads:
// ✅ TypeScript knows 'ping' exists and requires { timestamp: number }
await client.request('ping', { timestamp: Date.now() })
// ❌ TypeScript error: 'unknownEvent' doesn't exist
await client.request('unknownEvent', {})
// ❌ TypeScript error: missing required field 'timestamp'
await client.request('ping', {})
Validation
Zod validates all messages at runtime:
// ❌ Runtime error: validation failed
await client.request('ping', { timestamp: 'not a number' })
Auto-Reconnection
The client automatically reconnects with exponential backoff:
const client = createClient(url, events, {
maxRetries: Infinity,
minReconnectionDelay: 1000, // Start at 1 second
maxReconnectionDelay: 30000, // Max 30 seconds
reconnectionDelayGrowFactor: 1.3,
})
Message Queuing
Messages sent while disconnected are queued and sent when reconnected automatically.