Files
agent/.agent/skills/tech-stack/elysiajs/references/websocket.md

4.0 KiB

WebSocket

Basic WebSocket

import { Elysia } from 'elysia'

new Elysia()
  .ws('/chat', {
    message(ws, message) {
      ws.send(message)  // Echo back
    }
  })
  .listen(3000)

With Validation

import { Elysia, t } from 'elysia'

.ws('/chat', {
  body: t.Object({
    message: t.String(),
    username: t.String()
  }),
  response: t.Object({
    message: t.String(),
    timestamp: t.Number()
  }),
  message(ws, body) {
    ws.send({
      message: body.message,
      timestamp: Date.now()
    })
  }
})

Lifecycle Events

.ws('/chat', {
  open(ws) {
    console.log('Client connected')
  },
  message(ws, message) {
    console.log('Received:', message)
    ws.send('Echo: ' + message)
  },
  close(ws) {
    console.log('Client disconnected')
  },
  error(ws, error) {
    console.error('Error:', error)
  }
})

Broadcasting

const connections = new Set<any>()

.ws('/chat', {
  open(ws) {
    connections.add(ws)
  },
  message(ws, message) {
    // Broadcast to all connected clients
    for (const client of connections) {
      client.send(message)
    }
  },
  close(ws) {
    connections.delete(ws)
  }
})

With Authentication

.ws('/chat', {
  beforeHandle({ headers, status }) {
    const token = headers.authorization?.replace('Bearer ', '')
    if (!verifyToken(token)) {
      return status(401)
    }
  },
  message(ws, message) {
    ws.send(message)
  }
})

Room-Based Chat

const rooms = new Map<string, Set<any>>()

.ws('/chat/:room', {
  open(ws) {
    const room = ws.data.params.room
    if (!rooms.has(room)) {
      rooms.set(room, new Set())
    }
    rooms.get(room)!.add(ws)
  },
  message(ws, message) {
    const room = ws.data.params.room
    const clients = rooms.get(room)
    
    if (clients) {
      for (const client of clients) {
        client.send(message)
      }
    }
  },
  close(ws) {
    const room = ws.data.params.room
    const clients = rooms.get(room)
    
    if (clients) {
      clients.delete(ws)
      if (clients.size === 0) {
        rooms.delete(room)
      }
    }
  }
})

With State/Context

.ws('/chat', {
  open(ws) {
    ws.data.userId = generateUserId()
    ws.data.joinedAt = Date.now()
  },
  message(ws, message) {
    const response = {
      userId: ws.data.userId,
      message,
      timestamp: Date.now()
    }
    ws.send(response)
  }
})

Client Usage (Browser)

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

ws.onopen = () => {
  console.log('Connected')
  ws.send('Hello Server!')
}

ws.onmessage = (event) => {
  console.log('Received:', event.data)
}

ws.onerror = (error) => {
  console.error('Error:', error)
}

ws.onclose = () => {
  console.log('Disconnected')
}

Eden Treaty WebSocket

// Server
export const app = new Elysia()
  .ws('/chat', {
    message(ws, message) {
      ws.send(message)
    }
  })

export type App = typeof app

// Client
import { treaty } from '@elysiajs/eden'
import type { App } from './server'

const api = treaty<App>('localhost:3000')
const chat = api.chat.subscribe()

chat.subscribe((message) => {
  console.log('Received:', message)
})

chat.send('Hello!')

Headers in WebSocket

.ws('/chat', {
  header: t.Object({
    authorization: t.String()
  }),
  beforeHandle({ headers, status }) {
    const token = headers.authorization?.replace('Bearer ', '')
    if (!token) return status(401)
  },
  message(ws, message) {
    ws.send(message)
  }
})

Query Parameters

.ws('/chat', {
  query: t.Object({
    username: t.String()
  }),
  message(ws, message) {
    const username = ws.data.query.username
    ws.send(`${username}: ${message}`)
  }
})

// Client
const ws = new WebSocket('ws://localhost:3000/chat?username=john')

Compression

new Elysia({
  websocket: {
    perMessageDeflate: true
  }
})
  .ws('/chat', {
    message(ws, message) {
      ws.send(message)
    }
  })