Overview

Add real-time capabilities to your app using @fastify/websocket. Perfect for chat, live updates, and collaborative features.

Features

  • WebSocket integration with Fastify
  • Client connection tracking
  • Message type handling
  • Broadcast functionality
  • Connection metadata

Installation

npx hanma add fastify-socket-server --framework fastify

Dependencies

fastify, @fastify/websocket

Code

import Fastify, { FastifyInstance } from "fastify";
import websocket, { WebSocket } from "@fastify/websocket";

interface Client {
  id: string;
  socket: WebSocket;
  metadata?: Record<string, unknown>;
}

const clients = new Map<string, Client>();

function generateClientId(): string {
  return `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
}

export async function createSocketServer(): Promise<FastifyInstance> {
  const app = Fastify({
    logger: {
      level: process.env.LOG_LEVEL ?? "info",
    },
  });

  await app.register(websocket, {
    options: {
      maxPayload: 1048576, // 1MB
      clientTracking: true,
    },
  });

  // WebSocket route
  app.get("/ws", { websocket: true }, (socket, request) => {
    const clientId = generateClientId();
    
    const client: Client = {
      id: clientId,
      socket,
      metadata: {
        ip: request.ip,
        userAgent: request.headers["user-agent"],
        connectedAt: new Date().toISOString(),
      },
    };
    
    clients.set(clientId, client);
    app.log.info({ clientId }, "Client connected");

    // Send welcome message
    socket.send(JSON.stringify({
      type: "connected",
      clientId,
      timestamp: new Date().toISOString(),
    }));

    socket.on("message", (rawMessage) => {
      try {
        const message = JSON.parse(rawMessage.toString());
        app.log.debug({ clientId, message }, "Message received");
        
        // Handle different message types
        switch (message.type) {
          case "ping":
            socket.send(JSON.stringify({ type: "pong", timestamp: Date.now() }));
            break;
          case "broadcast":
            broadcast(message.data, clientId);
            break;
          default:
            socket.send(JSON.stringify({ 
              type: "echo", 
              data: message,
              timestamp: new Date().toISOString(),
            }));
        }
      } catch (err) {
        app.log.error({ clientId, error: err }, "Error processing message");
        socket.send(JSON.stringify({ 
          type: "error", 
          message: "Invalid message format" 
        }));
      }
    });

    socket.on("close", () => {
      clients.delete(clientId);
      app.log.info({ clientId }, "Client disconnected");
    });

    socket.on("error", (err) => {
      app.log.error({ clientId, error: err }, "WebSocket error");
      clients.delete(clientId);
    });
  });

  // Health check with connection count
  app.get("/health", async () => ({
    status: "healthy",
    connections: clients.size,
    timestamp: new Date().toISOString(),
  }));

  return app;
}

// Broadcast to all clients except sender
function broadcast(data: unknown, senderId?: string): void {
  const message = JSON.stringify({ type: "broadcast", data, from: senderId });
  clients.forEach((client) => {
    if (client.id !== senderId && client.socket.readyState === 1) {
      client.socket.send(message);
    }
  });
}

// Get all connected clients
export function getClients(): Client[] {
  return Array.from(clients.values());
}

// Send to specific client
export function sendToClient(clientId: string, data: unknown): boolean {
  const client = clients.get(clientId);
  if (client && client.socket.readyState === 1) {
    client.socket.send(JSON.stringify(data));
    return true;
  }
  return false;
}

// Usage:
// const app = await createSocketServer();
// await app.listen({ port: 3000 });

Usage

import { createSocketServer } from './socket-server';

const app = await createSocketServer();
await app.listen({ port: 3000 });

Dependencies

Frequently Asked Questions

How do I install WebSocket Server?
Run the following command: npx hanma add fastify-socket-server --framework fastify
What dependencies are required?
This snippet requires: fastify, @fastify/websocket. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to src/socket-server.ts.
Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.