Nuxt EasyWebSocket

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: {
clientSrcDir: 'socket',
serverSrcDir: 'socket',
delimiter: '/',
ws: {
maxReconnectAttempts: 10,
reconnectDelay: 5000,
reconnectOnClose: true,
},
externalSockets: {
'example-socket': {
url: 'wss://example.com/socket',
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
):
export default defineEasyWSEvent<{
message: string
user: string
timestamp: number
}>(({ data }) => {
console.log(`New message from ${data.user}: ${data.message}`)
})
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
export default defineEasyWSSConnection(({ peer }) => {
console.log(`New client connected: ${peer.peer.id}`)
peer.subscribe('announcements')
})
export default defineEasyWSSConnection(({ peer }) => {
console.log(`Client disconnected: ${peer.peer.id}`)
})
Defining Server-Side Event Handlers
export default defineEasyWSSEvent<{
text: string
userId: number
}>(async ({ data, peer }) => {
console.log(`Received message from user ${data.userId}: ${data.text}`)
await peer.send('chat', {
message: `Echo: ${data.text}`,
user: 'Server',
timestamp: Date.now()
})
await peer.publish('announcements', {
message: `User ${data.userId} sent a message`,
timestamp: Date.now()
})
})
Broadcasting to Connected Clients
export default defineEventHandler(async (event) => {
const body = await readBody(event)
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:
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:
declare module '#nuxt-easy-websocket/routes' {
interface EasyWSExternalRoutes {
'example-socket': {
ping: { timestamp: number }
}
}
}
This provides type safety when using useExternalWS
composable:
const externalWs = useExternalWS('example-socket')
externalWs.send('ping', { timestamp: Date.now() })
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:
export default defineEasyWSSEvent<{
topic: string
}>(async ({ data, peer }) => {
await peer.subscribe(data.topic)
await peer.send('notification', {
title: 'Subscribed',
message: `You are now subscribed to ${data.topic}`,
timestamp: Date.now()
})
})
Contribution
Local development
npm install
npm run dev:prepare
npm run dev
npm run dev:build
npm run lint
npm run test
npm run test:watch
npm run release
License
MIT License