WebSocket Examples — ODAC.JS Docs
Docs / ODAC.JS / Backend / Routing / WebSocket Examples

WebSocket Examples

Complete examples for common WebSocket use cases.

Echo Server

Simple echo server that sends back received messages:

// route/websocket.js
Odac.Route.ws('/echo', Odac => {
  Odac.ws.send({type: 'welcome', message: 'Connected!'})

  Odac.ws.on('message', data => {
    Odac.ws.send({type: 'echo', data})
  })
})

Client:

const ws = Odac.ws('/echo')
ws.on('message', data => console.log(data))
ws.send({message: 'Hello!'})

Authenticated Chat Room

Using auth.ws() (Recommended)

Odac.Route.auth.ws('/chat', async Odac => {
  const user = await Odac.Auth.user()

  Odac.ws.join('general')
  Odac.ws.data.user = user

  Odac.ws.to('general').send({
    type: 'user_joined',
    user: user.name
  })

  Odac.ws.on('message', data => {
    Odac.ws.to('general').send({
      type: 'message',
      user: user.name,
      text: data.text
    })
  })

  Odac.ws.on('close', () => {
    Odac.ws.to('general').send({
      type: 'user_left',
      user: user.name
    })
  })
})

Manual Authentication Check

Odac.Route.ws('/chat', async Odac => {
  const user = await Odac.Auth.user()
  
  if (!user) {
    Odac.ws.close(4001, 'Unauthorized')
    return
  }

  Odac.ws.join('general')
  Odac.ws.data.user = user

  Odac.ws.to('general').send({
    type: 'user_joined',
    user: user.name
  })

  Odac.ws.on('message', data => {
    Odac.ws.to('general').send({
      type: 'message',
      user: user.name,
      text: data.text
    })
  })

  Odac.ws.on('close', () => {
    Odac.ws.to('general').send({
      type: 'user_left',
      user: user.name
    })
  })
})

Client:

const chat = Odac.ws('/chat')

chat.on('message', data => {
  if (data.type === 'message') {
    console.log(`${data.user}: ${data.text}`)
  }
})

chat.send({text: 'Hello everyone!'})

Room-Based Chat with URL Parameters

Dynamic rooms using URL parameters:

Odac.Route.ws('/room/{roomId}', async Odac => {
  const {roomId} = Odac.Request.data.url
  const user = await Odac.Auth.user()

  if (!user) {
    Odac.ws.close(4001, 'Unauthorized')
    return
  }

  Odac.ws.join(roomId)
  Odac.ws.data.roomId = roomId

  Odac.ws.send({
    type: 'joined',
    room: roomId
  })

  Odac.ws.on('message', data => {
    Odac.ws.to(roomId).send({
      type: 'message',
      user: user.name,
      text: data.text,
      room: roomId
    })
  })
})

Client:

const room = Odac.ws('/room/gaming')
room.on('message', data => console.log(data))
room.send({text: 'Hi from gaming room!'})

Real-Time Notifications

User-specific notification system:

Odac.Route.ws('/notifications', async Odac => {
  const user = await Odac.Auth.user()

  if (!user) {
    Odac.ws.close(4001, 'Unauthorized')
    return
  }

  Odac.ws.join(`user-${user.id}`)
  Odac.ws.data.userId = user.id

  Odac.ws.send({
    type: 'connected',
    unreadCount: await getUnreadCount(user.id)
  })
})

// Send notification from anywhere in your app
async function notifyUser(userId, notification) {
  const wsServer = Odac.Route.wsServer
  wsServer.toRoom(`user-${userId}`, {
    type: 'notification',
    ...notification
  })
}

Client (with cross-tab sharing):

const notifications = Odac.ws('/notifications', {
  shared: true,
  autoReconnect: true
})

notifications.on('message', data => {
  if (data.type === 'notification') {
    showNotification(data.title, data.message)
  }
})

Broadcasting System

Broadcast messages to all connected clients:

Odac.Route.ws('/broadcast', Odac => {
  Odac.ws.on('message', data => {
    if (data.type === 'broadcast') {
      Odac.ws.broadcast({
        type: 'announcement',
        message: data.message,
        timestamp: Date.now()
      })
    }
  })
})

Client:

const broadcast = Odac.ws('/broadcast')

broadcast.on('message', data => {
  if (data.type === 'announcement') {
    alert(data.message)
  }
})

// Send to all clients
broadcast.send({
  type: 'broadcast',
  message: 'Server maintenance in 5 minutes'
})

Live Dashboard

Real-time data updates for dashboards:

Odac.Route.ws('/dashboard', async Odac => {
  const user = await Odac.Auth.user()

  if (!user || !user.isAdmin) {
    Odac.ws.close(4001, 'Unauthorized')
    return
  }

  const sendStats = async () => {
    const stats = await getSystemStats()
    Odac.ws.send({type: 'stats', data: stats})
  }

  sendStats()
  const interval = setInterval(sendStats, 5000)

  Odac.ws.on('close', () => {
    clearInterval(interval)
  })
})

Client:

const dashboard = Odac.ws('/dashboard')

dashboard.on('message', data => {
  if (data.type === 'stats') {
    updateDashboard(data.data)
  }
})

WebSocket with Middleware

Use middleware for rate limiting, authentication, or custom logic:

// middleware/rate-limit.js
const connections = new Map()

module.exports = async Odac => {
  const ip = Odac.Request.ip
  const now = Date.now()
  
  if (connections.has(ip)) {
    const lastConnection = connections.get(ip)
    if (now - lastConnection < 1000) {
      return false // Too many connections
    }
  }
  
  connections.set(ip, now)
  return true
}

// route/websocket.js
Odac.Route.use('rate-limit').ws('/chat', Odac => {
  Odac.ws.send({type: 'connected'})
})

Multiple Middleware:

Odac.Route.use('auth', 'rate-limit', 'log-connection').ws('/secure', Odac => {
  Odac.ws.send({type: 'authenticated'})
})

Multiplayer Game

Simple multiplayer game state synchronization:

Odac.Route.ws('/game/{gameId}', async Odac => {
  const {gameId} = Odac.Request.data.url
  const user = await Odac.Auth.user()

  if (!user) {
    Odac.ws.close(4001, 'Unauthorized')
    return
  }

  Odac.ws.join(`game-${gameId}`)
  Odac.ws.data.gameId = gameId
  Odac.ws.data.playerId = user.id

  Odac.ws.to(`game-${gameId}`).send({
    type: 'player_joined',
    playerId: user.id,
    name: user.name
  })

  Odac.ws.on('message', data => {
    switch (data.type) {
      case 'move':
        Odac.ws.to(`game-${gameId}`).send({
          type: 'player_moved',
          playerId: user.id,
          position: data.position
        })
        break
      
      case 'action':
        Odac.ws.to(`game-${gameId}`).send({
          type: 'player_action',
          playerId: user.id,
          action: data.action
        })
        break
    }
  })

  Odac.ws.on('close', () => {
    Odac.ws.to(`game-${gameId}`).send({
      type: 'player_left',
      playerId: user.id
    })
  })
})

Client:

const game = Odac.ws('/game/room-123')

game.on('message', data => {
  switch (data.type) {
    case 'player_joined':
      addPlayer(data.playerId, data.name)
      break
    case 'player_moved':
      updatePlayerPosition(data.playerId, data.position)
      break
    case 'player_action':
      handlePlayerAction(data.playerId, data.action)
      break
  }
})

// Send player movement
game.send({
  type: 'move',
  position: {x: 100, y: 200}
})