3.0.0 • Published 3 months ago

nuxt-easy-websocket v3.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 months ago

Nuxt EasyWebSocket

npm version npm downloads License Nuxt

A powerful Nuxt module providing seamless WebSocket integration with type-safe communication between client and server.

Features

  • šŸ”„ Auto-reconnection with configurable attempts and delay
  • 🌐 Multiple WebSocket connections (default internal + external)
  • šŸ” Type-safe messaging between client and server
  • 🧩 File-based routing for WebSocket events
  • šŸ”Œ Connection lifecycle hooks (open, close)
  • šŸ› ļø Simple API for sending and receiving messages
  • šŸ“ TypeScript support with full type inference

Installation

npx nuxi@latest module add nuxt-easy-websocket

Setup

Add nuxt-easy-websocket to the modules section of your nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['nuxt-easy-websocket'],
})

Configuration

Configure the module with these options in your nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['nuxt-easy-websocket'],
  easyWebSocket: {
    // Directory for client-side WebSocket event handlers (default: 'socket')
    clientSrcDir: 'socket',
    
    // Directory for server-side WebSocket event handlers (default: 'socket')
    serverSrcDir: 'socket',
    
    // Delimiter for route paths (default: '/')
    delimiter: '/',
    
    // WebSocket connection options
    ws: {
      // Maximum number of reconnection attempts (default: 10)
      maxReconnectAttempts: 10,
      
      // Delay between reconnection attempts in milliseconds (default: 5000)
      reconnectDelay: 5000,
      
      // Whether to automatically reconnect when connection closes (default: true)
      reconnectOnClose: true,
    },
    
    // External WebSocket connections (optional)
    externalSockets: {
      'example-socket': {
        url: 'wss://example.com/socket',
        // Override default connection options for this socket (optional)
        ws: {
          maxReconnectAttempts: 5,
          reconnectDelay: 3000,
        }
      }
    }
  }
})

Usage

Directory Structure

The module uses a file-based routing system similar to Nitro Routes:

šŸ“ project/
ā”œā”€ā”€ šŸ“ server/
│   ā”œā”€ā”€ šŸ“ socket/                  # Server-side WebSocket handlers
│   │   ā”œā”€ā”€ šŸ“„ open.ts              # Connection opened event
│   │   ā”œā”€ā”€ šŸ“„ close.ts             # Connection closed event
│   │   └── šŸ“ api/                 # API endpoints for client-to-server communication
│   │       ā”œā”€ā”€ šŸ“„ message.ts       # Handle 'message' events from client
│   │       └── šŸ“ user/            # API endpoints for client-to-server communication
│   │           └── šŸ“„ login.ts     # Handle 'user/login' events from client
│   │
ā”œā”€ā”€ šŸ“ socket/                      # Client-side WebSocket handlers
│   ā”œā”€ā”€ šŸ“„ chat.ts                  # Handle 'chat' events from server
│   └── šŸ“„ notification.ts          # Handle 'notification' events from server
│
ā”œā”€ā”€ šŸ“ example-socket/              # External WebSocket handlers (folder name must match the socket name)
│   └── šŸ“„ pong.ts                 # Handle 'pong' event from external socket

Important: For external WebSockets, the directory name must match the socket name defined in the configuration. For example, if you configured an external socket named example-socket, its event handlers should be placed in a directory named example-socket/.

Client-Side

Defining Client-Side Event Handlers

Create a file in your socket directory (or the configured clientSrcDir):

// socket/chat.ts
export default defineEasyWSEvent<{
  message: string
  user: string
  timestamp: number
}>(({ data }) => {
  console.log(`New message from ${data.user}: ${data.message}`)
  // Handle the chat message from the server
})

Using WebSocket in Components

<script setup>
// Access the WebSocket instance
const ws = useEasyWS()

// Connection status
const status = ws.connectionStatus
const isConnected = computed(() => status.value === 'connected')

// Send a message to the server
function sendMessage() {
  ws.send('api/message', {
    text: 'Hello world!',
    userId: 123
  })
}

// External WebSocket (if configured)
// Only available if you've defined external sockets in your config
const externalWs = useExternalWS('example-socket')
function sendExternalMessage() {
  externalWs.send('ping', { timestamp: Date.now() })
}
</script>

<template>
  <div>
    <p>Connection status: {{ status }}</p>
    <button @click="sendMessage" :disabled="!isConnected">
      Send Message
    </button>
    <button @click="ws.forceReconnect">
      Reconnect
    </button>
  </div>
</template>

Server-Side

Defining Connection Handlers

// server/socket/open.ts
export default defineEasyWSSConnection(({ peer }) => {
  console.log(`New client connected: ${peer.peer.id}`)
  
  // Subscribe client to topics
  peer.subscribe('announcements')
})

// server/socket/close.ts
export default defineEasyWSSConnection(({ peer }) => {
  console.log(`Client disconnected: ${peer.peer.id}`)
})

Defining Server-Side Event Handlers

// server/socket/api/message.ts
export default defineEasyWSSEvent<{
  text: string
  userId: number
}>(async ({ data, peer }) => {
  console.log(`Received message from user ${data.userId}: ${data.text}`)
  
  // Send a response back to the client
  await peer.send('chat', {
    message: `Echo: ${data.text}`,
    user: 'Server',
    timestamp: Date.now()
  })
  
  // Send to all subscribed clients
  await peer.publish('announcements', {
    message: `User ${data.userId} sent a message`,
    timestamp: Date.now()
  })
})

Broadcasting to Connected Clients

// server/api/broadcast.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  
  // Broadcast to all connected clients
  for (const peer of EasyWSSConnections.values()) {
    await peer.send('notification', {
      title: 'Broadcast',
      message: body.message,
      timestamp: Date.now()
    })
  }
  
  return { success: true }
})

Using External WebSockets

If you've configured external WebSocket connections, you can handle events from them:

// example-socket/pong.ts (folder name must match the socket name)
export default defineEasyWSEvent<{
  timestamp: number
}>(({ data }) => {
  const diffInMs = Date.now() - data.timestamp
  console.log(`Pong took: ${diffInMs}ms`)
})

Type Augmentation for External WebSockets

To get type safety with external WebSockets, augment the module types by creating a declaration file:

// project/types/external-ws.d.ts
declare module '#nuxt-easy-websocket/routes' {
  interface EasyWSExternalRoutes {
    'example-socket': {
      ping: { timestamp: number }
    }
  }
}

This provides type safety when using useExternalWS composable:

// Type-safe usage with the augmented types
const externalWs = useExternalWS('example-socket')
externalWs.send('ping', { timestamp: Date.now() }) // Fully typed!

TypeScript Support

The module provides full TypeScript support with type inference for your WebSocket events. The types are automatically generated based on your file structure.

Advanced Usage

Accessing Connection State

<script setup>
const ws = useEasyWS()

// Read-only state object
const state = ws.state

// Computed helpers
const isConnected = computed(() => ws.connectionStatus.value === 'connected')
const maxAttemptsReached = ws.maxReconnectAttemptsReached
</script>

<template>
  <div>
    <p>Status: {{ ws.connectionStatus }}</p>
    <p v-if="state.lastError">Last error: {{ state.lastError }}</p>
    <p v-if="state.reconnectCountdown">
      Reconnecting in {{ state.reconnectCountdown }}s...
    </p>
    <p v-if="maxAttemptsReached">
      Max reconnection attempts reached
    </p>
  </div>
</template>

Server-Side Topic Subscriptions

WebSocket clients can subscribe to topics for pub/sub functionality:

// server/socket/api/subscribe.ts
export default defineEasyWSSEvent<{
  topic: string
}>(async ({ data, peer }) => {
  // Subscribe client to the requested topic
  await peer.subscribe(data.topic)
  
  // Confirm subscription
  await peer.send('notification', {
    title: 'Subscribed',
    message: `You are now subscribed to ${data.topic}`,
    timestamp: Date.now()
  })
})

Contribution

License

MIT License

3.0.0

3 months ago

2.3.4

5 months ago

2.3.3

5 months ago

2.3.2

5 months ago

2.3.1

5 months ago

2.3.0

5 months ago

2.2.1

5 months ago

2.2.0

5 months ago

2.1.3

5 months ago

2.1.2

5 months ago

2.1.1

5 months ago

2.1.0

5 months ago

2.0.0

5 months ago

1.2.1

5 months ago

1.2.0

6 months ago