Skip to main content ホーム クリエイター patricio0312rev Skills websocket-realtime-builder
websocket-realtime-builder Implements real-time features using WebSockets with Socket.io, rooms, authentication, and reconnection handling. Use when users request "real-time updates", "WebSocket", "Socket.io", "live chat", or "push notifications".
インストールへ移動 Skills Marketplace コミュニティが作成したAIスキルを発見・探索
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/patricio0312rev/skills --skill websocket-realtime-builderコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Zipをダウンロード ダウンロード中... name websocket-realtime-builder description Implements real-time features using WebSockets with Socket.io, rooms, authentication, and reconnection handling. Use when users request "real-time updates", "WebSocket", "Socket.io", "live chat", or "push notifications".
WebSocket Realtime Builder
Build real-time applications with WebSockets and Socket.io.
Core Workflow
Choose library : Socket.io vs native WebSocket
Setup server : Configure WebSocket server
Add authentication : Validate connections
Implement rooms : Group connections
Handle events : Define event handlers
Add reconnection : Handle disconnects gracefully
Installation
npm install socket.io
npm install socket.io-client
Server Setup
Basic Socket.io Server
import express from 'express' ;
import { createServer } from 'http' ;
import { Server , Socket } from 'socket.io' ;
import { verifyToken } from './auth' ;
const app = ();
httpServer = (app);
io = (httpServer, {
: {
: process. . ,
: ,
},
: ,
: ,
});
io. ( (socket, next) => {
token = socket. . . ;
(!token) {
( ( ));
}
{
user = (token);
socket. . = user;
();
} (err) {
( ( ));
}
});
io. ( , {
user = socket. . ;
. ( );
socket. ( );
socket. ( , {
. ( );
});
});
httpServer. ( , {
. ( );
});
{ io };
express
const
createServer
const
new
Server
cors
origin
env
CLIENT_URL
credentials
true
pingInterval
25000
pingTimeout
60000
use
async
const
handshake
auth
token
if
return
next
new
Error
'Authentication required'
try
const
await
verifyToken
data
user
next
catch
next
new
Error
'Invalid token'
on
'connection'
(socket : Socket ) =>
const
data
user
console
log
`User connected: ${user.id} `
join
`user:${user.id} `
on
'disconnect'
() =>
console
log
`User disconnected: ${user.id} `
listen
3001
() =>
console
log
'Socket.io server running on port 3001'
export
Namespaces and Rooms
import { Server , Socket } from 'socket.io' ;
export function setupChatNamespace (io : Server ) {
const chatNamespace = io.of ('/chat' );
chatNamespace.on ('connection' , (socket : Socket ) => {
const user = socket.data .user ;
socket.on ('join-room' , async (roomId : string ) => {
const canAccess = await canAccessRoom (user.id , roomId);
if (!canAccess) {
socket.emit ('error' , { message : 'Access denied' });
return ;
}
socket.join (`room:${roomId} ` );
socket.to (`room:${roomId} ` ).emit ('user-joined' , {
userId : user.id ,
name : user.name ,
});
});
socket.on ('leave-room' , (roomId : string ) => {
socket.leave (`room:${roomId} ` );
socket.to (`room:${roomId} ` ).emit ('user-left' , {
userId : user.id ,
});
});
socket.on ('send-message' , async (data : { roomId : string ; content : string }) => {
const { roomId, content } = data;
const message = await db.message .create ({
data : {
roomId,
authorId : user.id ,
content,
},
include : { author : true },
});
chatNamespace.to (`room:${roomId} ` ).emit ('new-message' , {
id : message.id ,
content : message.content ,
author : {
id : user.id ,
name : user.name ,
},
createdAt : message.createdAt ,
});
});
socket.on ('typing-start' , (roomId : string ) => {
socket.to (`room:${roomId} ` ).emit ('user-typing' , {
userId : user.id ,
name : user.name ,
});
});
socket.on ('typing-stop' , (roomId : string ) => {
socket.to (`room:${roomId} ` ).emit ('user-stopped-typing' , {
userId : user.id ,
});
});
});
return chatNamespace;
}
Event Emitters
import { io } from '../server' ;
export class NotificationService {
static sendToUser (userId : string , event : string , data : any ) {
io.to (`user:${userId} ` ).emit (event, data);
}
static sendToUsers (userIds : string [], event : string , data : any ) {
userIds.forEach ((userId ) => {
io.to (`user:${userId} ` ).emit (event, data);
});
}
static broadcast (event : string , data : any ) {
io.emit (event, data);
}
static sendToRoom (roomId : string , event : string , data : any ) {
io.to (`room:${roomId} ` ).emit (event, data);
}
static notifyNewOrder (order : Order ) {
this .sendToUser (order.customerId , 'order:created' , {
orderId : order.id ,
status : order.status ,
});
io.to ('role:admin' ).emit ('admin:new-order' , {
orderId : order.id ,
customer : order.customerName ,
total : order.total ,
});
}
}
Client Setup
React Client Hook
import { useEffect, useRef, useState, useCallback } from 'react' ;
import { io, Socket } from 'socket.io-client' ;
import { useAuth } from './useAuth' ;
interface UseSocketOptions {
namespace ?: string ;
autoConnect ?: boolean ;
}
export function useSocket (options : UseSocketOptions = {} ) {
const { namespace = '/' , autoConnect = true } = options;
const { token } = useAuth ();
const socketRef = useRef<Socket | null >(null );
const [isConnected, setIsConnected] = useState (false );
const [error, setError] = useState<string | null >(null );
useEffect (() => {
if (!token || !autoConnect) return ;
const socket = io (`${process.env.NEXT_PUBLIC_WS_URL} ${namespace} ` , {
auth : { token },
reconnection : true ,
reconnectionAttempts : 5 ,
reconnectionDelay : 1000 ,
reconnectionDelayMax : 5000 ,
});
socket.on ('connect' , () => {
setIsConnected (true );
setError (null );
});
socket.on ('disconnect' , () => {
setIsConnected (false );
});
socket.on ('connect_error' , (err ) => {
setError (err.message );
setIsConnected (false );
});
socketRef.current = socket;
return () => {
socket.disconnect ();
};
}, [token, namespace, autoConnect]);
const emit = useCallback ((event : string , data ?: any ) => {
socketRef.current ?.emit (event, data);
}, []);
const on = useCallback ((event : string , handler : (...args: any []) => void ) => {
socketRef.current ?.on (event, handler);
return () => {
socketRef.current ?.off (event, handler);
};
}, []);
const off = useCallback ((event : string , handler ?: (...args: any []) => void ) => {
socketRef.current ?.off (event, handler);
}, []);
return {
socket : socketRef.current ,
isConnected,
error,
emit,
on,
off,
};
}
Chat Hook
import { useEffect, useState, useCallback } from 'react' ;
import { useSocket } from './useSocket' ;
interface Message {
id : string ;
content : string ;
author : { id : string ; name : string };
createdAt : string ;
}
interface TypingUser {
userId : string ;
name : string ;
}
export function useChat (roomId : string ) {
const { socket, isConnected, emit, on } = useSocket ({ namespace : '/chat' });
const [messages, setMessages] = useState<Message []>([]);
const [typingUsers, setTypingUsers] = useState<TypingUser []>([]);
useEffect (() => {
if (isConnected && roomId) {
emit ('join-room' , roomId);
return () => {
emit ('leave-room' , roomId);
};
}
}, [isConnected, roomId, emit]);
useEffect (() => {
const unsubMessage = on ('new-message' , (message : Message ) => {
setMessages ((prev ) => [...prev, message]);
});
const unsubTyping = on ('user-typing' , (user : TypingUser ) => {
setTypingUsers ((prev ) => {
if (prev.some ((u ) => u.userId === user.userId )) return prev;
return [...prev, user];
});
});
const unsubStopTyping = on ('user-stopped-typing' , ({ userId }: { userId: string } ) => {
setTypingUsers ((prev ) => prev.filter ((u ) => u.userId !== userId));
});
return () => {
unsubMessage ();
unsubTyping ();
unsubStopTyping ();
};
}, [on]);
const sendMessage = useCallback ((content : string ) => {
emit ('send-message' , { roomId, content });
}, [emit, roomId]);
const startTyping = useCallback (() => {
emit ('typing-start' , roomId);
}, [emit, roomId]);
const stopTyping = useCallback (() => {
emit ('typing-stop' , roomId);
}, [emit, roomId]);
return {
messages,
typingUsers,
sendMessage,
startTyping,
stopTyping,
isConnected,
};
}
Chat Component
'use client' ;
import { useState, useRef, useEffect } from 'react' ;
import { useChat } from '@/hooks/useChat' ;
export function ChatRoom ({ roomId }: { roomId: string } ) {
const {
messages,
typingUsers,
sendMessage,
startTyping,
stopTyping,
isConnected,
} = useChat (roomId);
const [input, setInput] = useState ('' );
const messagesEndRef = useRef<HTMLDivElement >(null );
const typingTimeoutRef = useRef<NodeJS .Timeout >();
useEffect (() => {
messagesEndRef.current ?.scrollIntoView ({ behavior : 'smooth' });
}, [messages]);
const handleInputChange = (e : React .ChangeEvent <HTMLInputElement > ) => {
setInput (e.target .value );
startTyping ();
clearTimeout (typingTimeoutRef.current );
typingTimeoutRef.current = setTimeout (stopTyping, 2000 );
};
const handleSubmit = (e : React .FormEvent ) => {
e.preventDefault ();
if (!input.trim ()) return ;
sendMessage (input);
setInput ('' );
stopTyping ();
};
if (!isConnected) {
return <div > Connecting...</div > ;
}
return (
<div className ="flex flex-col h-full" >
<div className ="flex-1 overflow-y-auto p-4 space-y-4" >
{messages.map((message) => (
<div key ={message.id} className ="flex gap-2" >
<span className ="font-semibold" > {message.author.name}:</span >
<span > {message.content}</span >
</div >
))}
<div ref ={messagesEndRef} />
</div >
{typingUsers.length > 0 && (
<div className ="px-4 py-2 text-sm text-gray-500" >
{typingUsers.map((u) => u.name).join(', ')}{' '}
{typingUsers.length === 1 ? 'is' : 'are'} typing...
</div >
)}
<form onSubmit ={handleSubmit} className ="p-4 border-t" >
<input
type ="text"
value ={input}
onChange ={handleInputChange}
placeholder ="Type a message..."
=
/>
);
}
Real-time Notifications
import { useEffect, useState } from 'react' ;
import { useSocket } from './useSocket' ;
import { toast } from 'sonner' ;
interface Notification {
id : string ;
type : string ;
title : string ;
message : string ;
createdAt : string ;
read : boolean ;
}
export function useNotifications ( ) {
const { on, isConnected } = useSocket ();
const [notifications, setNotifications] = useState<Notification []>([]);
const [unreadCount, setUnreadCount] = useState (0 );
useEffect (() => {
const unsubscribe = on ('notification' , (notification : Notification ) => {
setNotifications ((prev ) => [notification, ...prev]);
setUnreadCount ((prev ) => prev + 1 );
toast (notification.title , {
description : notification.message ,
});
});
return unsubscribe;
}, [on]);
const markAsRead = (id : string ) => {
setNotifications ((prev ) =>
prev.map ((n ) => (n.id === id ? { ...n, read : true } : n))
);
setUnreadCount ((prev ) => Math .max (0 , prev - 1 ));
};
const markAllAsRead = ( ) => {
setNotifications ((prev ) => prev.map ((n ) => ({ ...n, read : true })));
setUnreadCount (0 );
};
return {
notifications,
unreadCount,
markAsRead,
markAllAsRead,
isConnected,
};
}
Presence System
import { Server , Socket } from 'socket.io' ;
interface UserPresence {
odlineserId : string ;
name : string ;
status : 'online' | 'away' | 'busy' ;
lastSeen : Date ;
}
const onlineUsers = new Map <string , UserPresence >();
export function setupPresence (io : Server ) {
io.on ('connection' , (socket : Socket ) => {
const user = socket.data .user ;
onlineUsers.set (user.id , {
odlineserId : user.id ,
name : user.name ,
status : 'online' ,
lastSeen : new Date (),
});
io.emit ('presence:update' , {
userId : user.id ,
status : 'online' ,
});
socket.emit ('presence:list' , Array .from (onlineUsers.values ()));
socket.on ('presence:status' , (status : 'online' | 'away' | 'busy' ) => {
const presence = onlineUsers.get (user.id );
if (presence) {
presence.status = status;
io.emit ('presence:update' , { userId : user.id , status });
}
});
socket.on ('disconnect' , () => {
onlineUsers.delete (user.id );
io.emit ('presence:update' , {
userId : user.id ,
status : 'offline' ,
});
});
});
}
Error Handling & Reconnection
const socket = io (WS_URL , {
auth : { token },
reconnection : true ,
reconnectionAttempts : 10 ,
reconnectionDelay : 1000 ,
reconnectionDelayMax : 10000 ,
timeout : 20000 ,
});
socket.on ('connect_error' , (error ) => {
if (error.message === 'Authentication required' ) {
router.push ('/login' );
} else {
console .error ('Connection error:' , error);
}
});
socket.on ('reconnect' , (attemptNumber ) => {
console .log (`Reconnected after ${attemptNumber} attempts` );
socket.emit ('sync-state' );
});
socket.on ('reconnect_failed' , () => {
console .error ('Failed to reconnect' );
toast.error ('Connection lost. Please refresh the page.' );
});
Best Practices
Authenticate connections : Validate tokens on connect
Use rooms : Group users logically
Handle reconnection : Re-sync state after reconnect
Debounce events : Prevent flooding (e.g., typing indicator)
Clean up listeners : Remove on component unmount
Acknowledge important events : Confirm critical messages
Use namespaces : Separate concerns (chat, notifications)
Scale with Redis : Use Redis adapter for multiple servers
Output Checklist Every WebSocket implementation should include:
className
"w-full px-4 py-2 border rounded"
</form >
</div >