A Nuxt module aimed to simplify real-time communication with built-in automatic topic subscriptions, reactive shared state management, and type safety.
useWS: A WebSocket implementation with built-in shared state management, topic subscriptions, and type safety.defineWSHandler: wraps Nitro'sdefineWebSocketHandlerto provide additional configuration, hooks and automatic topic subscription.
Install the module to your Nuxt application with one command:
npx nuxi module add nuxt-wsIdeally you should set your default path, based on where you will place your WebSocket api handlers:
export default defineNuxtConfig({
  modules: ['nuxt-ws'],
  ws: {
    path: '/_ws', // default undefined
  }
})A WebSocket implementation with built-in shared state management, topic subscriptions, and type safety.
- 🔄 Automatic reconnection
 - 📦 Built-in shared state management per-topic
 - 🤖 Build-in validation with Standard Schema
 - 🔐 Type-safe messages and topics
 - 📢 Hooking system to trigger messages globaly
 - ⛓️ Integrates easily with other Nuxt modules
 
Take in example the following setup:
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-ws'],
  ws: {
    route: '/_ws',             // WebSocket endpoint to auto-connect
    topics: {
      defaults: ['session'],   // Auto-subscribed topics
    }
  }
})Client-Side (pages/chat.client.vue):
<template>
  <div>
    <div v-if="status === 'OPEN'">
      Connected, Users: {{ states['session'].users }}
    </div>
    <div v-else>
      Disconnected
    </div>
    <div v-for="({ user, text }, key) in states['chat']" :key>
      {{ user }}: {{ text }}
    </div>
    <input v-model="textRef" @keyup.enter="sendMessage()">
  </div>
</template>
<script setup lang="ts">
const textRef = ref('')
const { states, status, send } = useWS<{
  chat: {
    user?: string
    text: string
  }[]
  session: {
    users: number
  }
}>(['chat'])
function sendMessage() {
  send('publish', 'chat', {
    text: textRef.value,
  })
  textRef.value = ''
}
</script>Server-Side (server/routes/_ws.ts):
import * as v from 'valibot'
export default defineWSHandler({
  async open(peer) {
    // Update peer with 'chat' data from storage
    const chat = await useStorage('ws').getItem('chat')
    if (chat)
      peer.send(JSON.stringify({
        topic: 'chat',
        payload: chat,
      }), { compress: true })
    // Update everyone's session metadata
    const payload = JSON.stringify({ topic: 'session', payload: { users: peer.peers.size } })
    peer.send(payload, { compress: true })
    peer.publish('session', payload, { compress: true })
  },
  async message(peer, message) {
    // Validate the incoming chat message
    const parsedMessage = await wsValidateMessage( // built-in validation util
      v.object({
        type: v.literal('publish'),
        topic: v.literal('chat'),
        payload: v.object({ text: v.string() }),
      }),
      message,
    )
    // Update chat data in storage
    const mem = useStorage('ws')
    const { topic, payload } = parsedMessage
    const _chat = await mem.getItem<Array<{ user: string, text: string }>>('chat') || []
    const newChat = [..._chat, { ...payload, user: peer.id }]
    await mem.setItem(topic, newChat)
    // Broadcast the updated chat to everyone
    peer.send(JSON.stringify({ topic, payload: newChat }), { compress: true })
    peer.publish(topic, JSON.stringify({ topic, payload: newChat }), { compress: true })
  },
  close(peer) {
    // Update everyone's session metadata
    peer.publish(
      'session',
      JSON.stringify({
        topic: 'session',
        payload: {
          users: peer.peers.size,
        },
      }),
      { compress: true },
    )
  },
})Local development
# Install dependencies
pnpm install
# Generate type stubs
pnpm run dev:prepare
# Develop with the playground
pnpm run dev
# Build the playground
pnpm run dev:build
# Run ESLint
pnpm run lint
# Run Vitest
pnpm run test
pnpm run test:watchPublished under the MIT license.