WebSockets
Socket.IO com rooms, broadcast, notificacoes e setup por framework.
O PlazerCLI configura Socket.IO para comunicacao em tempo real. O setup inclui autenticacao por JWT no handshake, rooms por usuario/organizacao e broadcast de eventos.
Setup NestJS
No NestJS, o PlazerCLI gera um Gateway completo:
// apps/api/src/websockets/events.gateway.ts
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { JwtService } from '@nestjs/jwt';
@WebSocketGateway({
cors: { origin: process.env.FRONTEND_URL || 'http://localhost:3000' },
namespace: '/events',
})
export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server;
constructor(private jwtService: JwtService) {}
async handleConnection(client: Socket) {
try {
const token = client.handshake.auth.token;
const payload = this.jwtService.verify(token);
client.data.userId = payload.sub;
// Entrar na room pessoal
await client.join(`user:${payload.sub}`);
// Se tiver organizacao, entrar na room da org
if (payload.orgId) {
await client.join(`org:${payload.orgId}`);
}
} catch {
client.disconnect();
}
}
handleDisconnect(client: Socket) {
console.log(`Client disconnected: ${client.id}`);
}
@SubscribeMessage('message')
handleMessage(client: Socket, payload: { room: string; data: any }) {
this.server.to(payload.room).emit('message', {
userId: client.data.userId,
data: payload.data,
timestamp: new Date().toISOString(),
});
}
// Metodo utilitario para enviar notificacoes
sendNotification(userId: string, notification: any) {
this.server.to(`user:${userId}`).emit('notification', notification);
}
}
Setup Express / Fastify
// apps/api/src/websockets/socket.ts
import { Server } from 'socket.io';
import jwt from 'jsonwebtoken';
export function setupWebSockets(httpServer: any) {
const io = new Server(httpServer, {
cors: { origin: process.env.FRONTEND_URL || 'http://localhost:3000' },
});
// Middleware de autenticacao
io.use((socket, next) => {
const token = socket.handshake.auth.token;
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;
socket.data.userId = payload.sub;
socket.data.orgId = payload.orgId;
next();
} catch {
next(new Error('Authentication error'));
}
});
io.on('connection', (socket) => {
// Rooms automaticas
socket.join(`user:${socket.data.userId}`);
if (socket.data.orgId) {
socket.join(`org:${socket.data.orgId}`);
}
socket.on('message', (payload) => {
io.to(payload.room).emit('message', {
userId: socket.data.userId,
data: payload.data,
timestamp: new Date().toISOString(),
});
});
});
return io;
}
// No server principal:
import { createServer } from 'http';
const httpServer = createServer(app);
const io = setupWebSockets(httpServer);
httpServer.listen(3001);
Cliente (Frontend)
// apps/web/src/lib/socket.ts
import { io, Socket } from 'socket.io-client';
let socket: Socket | null = null;
export function connectSocket(token: string): Socket {
if (socket?.connected) return socket;
socket = io(process.env.NEXT_PUBLIC_API_URL + '/events', {
auth: { token },
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
});
socket.on('connect', () => console.log('Socket connected'));
socket.on('disconnect', () => console.log('Socket disconnected'));
return socket;
}
export function getSocket(): Socket | null {
return socket;
}
// Uso em um componente React:
import { useEffect, useState } from 'react';
import { connectSocket, getSocket } from '@/lib/socket';
export function useNotifications(token: string) {
const [notifications, setNotifications] = useState([]);
useEffect(() => {
const socket = connectSocket(token);
socket.on('notification', (data) => {
setNotifications((prev) => [data, ...prev]);
});
return () => { socket.off('notification'); };
}, [token]);
return notifications;
}