Use Cases (अपडेट: 3/6/2026)

Claude Code से WebSocket चैट ऐप बनाएं

Claude Code से सुरक्षित WebSocket चैट बनाएं: प्रमाणीकरण, पुनःकनेक्शन, दर सीमा और चलने वाला Node.js कोड।

Claude Code से WebSocket चैट ऐप बनाएं

WebSocket चैट छोटा काम लगता है, लेकिन इसी में उत्पादन जैसी कई बातें आ जाती हैं: कनेक्शन पर प्रमाणीकरण, नेटवर्क टूटने पर दोबारा जुड़ना, कमरे के भीतर संदेश भेजना, स्पैम रोकने के लिए सीमा, JSON जांच और एक छोटा smoke test। Claude Code तेज़ी से फाइलें बना सकता है, लेकिन अगर निर्देश सिर्फ “chat app बना दो” है, तो अक्सर बिना सुरक्षा वाला demo मिलेगा।

इस गाइड में ब्राउजर की WebSocket API और Node.js के ws package से ऐसा उदाहरण बनाया गया है जिसे सीधे चलाया जा सकता है। WebSocket में पहले HTTP upgrade होता है, फिर ब्राउजर और server एक ही लंबे कनेक्शन पर दोनों तरफ से संदेश भेज सकते हैं। आधिकारिक संदर्भ के लिए MDN WebSocket API, Writing WebSocket client applications, RFC 6455, Node.js HTTP upgrade event, ws README और OWASP WebSocket testing guide देखें। Claude Code workflow के लिए Claude Code docs रखें।

आगे पढ़ने के लिए Claude Code API development, Claude Code code review और security best practices उपयोगी हैं।

तीन व्यावहारिक उपयोग

पहला उपयोग छोटा समुदाय या support chat है। Room, name, text और recent history से शुरुआत हो जाती है। लेकिन प्रमाणीकरण और भेजने की सीमा न हो, तो एक script server को flood कर सकती है।

दूसरा उपयोग live dashboard है। Build status, payment event, stock update या job queue status तुरंत ब्राउजर में दिख सकते हैं। यहाँ सवाल यह है कि पुराने events कब हटाने हैं, क्योंकि browser WebSocket API में automatic strong backpressure नहीं है।

तीसरा उपयोग article, course या paid material के बाद question room है। पाठक सामग्री पढ़कर सवाल पूछ सकता है, और team उसे template, product या consultation page तक ले जा सकती है। यह आय मार्ग में मदद करता है, पर logs, moderation और personal data rules पहले लिखने पड़ते हैं।

उपयोगWebSocket क्यों उपयुक्त हैपहले क्या तय करें
छोटा chatकम विलंब वाले दोतरफा messagesAuth, rooms, history
Live dashboardServer update push कर सकता हैDrop policy, reconnect
Learning supportArticle से conversation बनती हैLogs, moderation, CTA

Architecture

Fan-out यानी एक incoming message को उसी room के सभी connections तक भेजना। Heartbeat यानी लंबे connection के जीवित होने की नियमित जांच।

flowchart LR
  BrowserA["Browser tab A"] -->|ws:// /chat| Node["Node.js server"]
  BrowserB["Browser tab B"] -->|ws:// /chat| Node
  Smoke["smoke-client.mjs"] -->|ws:// /chat| Node
  Node --> Auth["token and Origin check"]
  Node --> Rooms["room registry"]
  Rooms --> Fanout["fan-out to room peers"]
  Node --> History["last 50 messages"]
  Node --> Limit["rate limit and max payload"]

Claude Code के लिए निर्देश

Node.js 20+ और ws package से minimal WebSocket chat बनाइए।

Requirements:
- सिर्फ server.js, index.html, smoke-client.mjs और package.json
- http://localhost:8080 पर chat UI serve हो
- /chat WebSocket upgrade में token और Origin validate हों
- room के हिसाब से fan-out हो
- memory में सिर्फ latest 50 messages रहें
- हर connection पर 10 seconds में 20 messages की limit हो
- message text 500 characters तक सीमित हो
- browser client close के बाद exponential backoff से reconnect करे
- send से पहले readyState और bufferedAmount check हों
- smoke-client.mjs connect करे, एक message भेजे और success से exit करे

न करें:
- इसे Socket.IO में न बदलें
- unauthenticated connection accept न करें
- JSON input बिना validation use न करें
- memory history को production persistence की तरह न लिखें

स्थानीय सेटअप

mkdir ws-chat-demo
cd ws-chat-demo
npm init -y
npm install ws
npm run start

package.json:

{
  "type": "module",
  "scripts": {
    "start": "node server.js",
    "smoke": "node smoke-client.mjs"
  },
  "dependencies": {
    "ws": "^8.18.3"
  },
  "engines": {
    "node": ">=20"
  }
}

server.js

import { randomUUID } from "node:crypto";
import { readFile } from "node:fs/promises";
import { createServer } from "node:http";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { WebSocket, WebSocketServer } from "ws";

const PORT = Number(process.env.PORT ?? 8080);
const AUTH_TOKEN = process.env.CHAT_TOKEN ?? "dev-token";
const MAX_MESSAGE_LENGTH = 500;
const HISTORY_LIMIT = 50;
const RATE_WINDOW_MS = 10_000;
const RATE_LIMIT = 20;
const CLIENT_FILE = join(fileURLToPath(new URL(".", import.meta.url)), "index.html");
const ALLOWED_ORIGINS = new Set(
  (process.env.ALLOWED_ORIGINS ?? `http://localhost:${PORT},http://127.0.0.1:${PORT}`)
    .split(",")
    .map((value) => value.trim())
    .filter(Boolean)
);

const rooms = new Map();
const histories = new Map();
const clients = new Map();

const server = createServer(async (request, response) => {
  if (request.url === "/" || request.url === "/index.html") {
    try {
      const html = await readFile(CLIENT_FILE, "utf8");
      response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
      response.end(html);
    } catch {
      response.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
      response.end("index.html was not found");
    }
    return;
  }

  if (request.url === "/healthz") {
    response.writeHead(200, { "content-type": "application/json" });
    response.end(JSON.stringify({ ok: true, clients: clients.size }));
    return;
  }

  response.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
  response.end("not found");
});

const wss = new WebSocketServer({ noServer: true, maxPayload: 2048 });

server.on("upgrade", (request, socket, head) => {
  const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "localhost"}`);
  if (url.pathname !== "/chat") {
    rejectUpgrade(socket, 404, "Not Found");
    return;
  }

  const origin = request.headers.origin ?? "";
  if (!ALLOWED_ORIGINS.has(origin)) {
    rejectUpgrade(socket, 403, "Forbidden");
    return;
  }

  const token = url.searchParams.get("token");
  if (token !== AUTH_TOKEN) {
    rejectUpgrade(socket, 401, "Unauthorized");
    return;
  }

  const context = {
    name: cleanName(url.searchParams.get("name")),
    room: cleanRoom(url.searchParams.get("room"))
  };

  wss.handleUpgrade(request, socket, head, (ws) => {
    wss.emit("connection", ws, request, context);
  });
});

wss.on("connection", (ws, request, context) => {
  const client = {
    id: randomUUID(),
    name: context.name,
    room: context.room,
    isAlive: true,
    rateResetAt: Date.now() + RATE_WINDOW_MS,
    messagesInWindow: 0
  };

  clients.set(ws, client);
  joinRoom(ws, client.room);

  send(ws, { type: "system", message: `connected as ${client.name}`, clientId: client.id });
  send(ws, { type: "history", messages: histories.get(client.room) ?? [] });
  broadcast(client.room, { type: "presence", message: `${client.name} joined`, online: roomSize(client.room) });

  ws.on("pong", () => {
    client.isAlive = true;
  });

  ws.on("message", (raw, isBinary) => {
    if (isBinary) {
      ws.close(1003, "text messages only");
      return;
    }

    if (!consumeQuota(client)) {
      send(ws, { type: "error", code: "rate_limited", message: "Too many messages. Wait a moment." });
      return;
    }

    const textBody = raw.toString("utf8");
    if (textBody.length > MAX_MESSAGE_LENGTH) {
      send(ws, { type: "error", code: "too_large", message: `Message must be ${MAX_MESSAGE_LENGTH} characters or less.` });
      return;
    }

    let event;
    try {
      event = JSON.parse(textBody);
    } catch {
      send(ws, { type: "error", code: "bad_json", message: "Send JSON such as {\"type\":\"message\",\"text\":\"hi\"}." });
      return;
    }

    if (event.type !== "message") {
      send(ws, { type: "error", code: "bad_type", message: "Only message events are accepted." });
      return;
    }

    const text = String(event.text ?? "").trim();
    if (!text) {
      send(ws, { type: "error", code: "empty", message: "Message text is required." });
      return;
    }

    const message = {
      id: randomUUID(),
      room: client.room,
      from: client.name,
      text,
      sentAt: new Date().toISOString()
    };

    remember(client.room, message);
    broadcast(client.room, { type: "message", message });
  });

  ws.on("close", () => {
    leaveRoom(ws);
    clients.delete(ws);
    broadcast(client.room, { type: "presence", message: `${client.name} left`, online: roomSize(client.room) });
  });

  ws.on("error", (error) => {
    console.error("websocket error", error);
  });
});

const heartbeat = setInterval(() => {
  for (const ws of wss.clients) {
    const client = clients.get(ws);
    if (!client) continue;
    if (!client.isAlive) {
      ws.terminate();
      continue;
    }
    client.isAlive = false;
    ws.ping();
  }
}, 30_000);

server.listen(PORT, () => {
  console.log(`Chat demo: http://localhost:${PORT}`);
  console.log(`Token: ${AUTH_TOKEN}`);
});

process.on("SIGINT", () => {
  clearInterval(heartbeat);
  wss.close();
  server.close(() => process.exit(0));
});

function rejectUpgrade(socket, statusCode, message) {
  socket.write(`HTTP/1.1 ${statusCode} ${message}\r\nConnection: close\r\n\r\n`);
  socket.destroy();
}

function cleanName(value) {
  const name = String(value ?? "guest").trim().slice(0, 24);
  return /^[\w -]+$/.test(name) ? name : "guest";
}

function cleanRoom(value) {
  const room = String(value ?? "lobby").trim().slice(0, 32);
  return /^[a-zA-Z0-9_-]+$/.test(room) ? room : "lobby";
}

function joinRoom(ws, room) {
  if (!rooms.has(room)) rooms.set(room, new Set());
  rooms.get(room).add(ws);
}

function leaveRoom(ws) {
  const client = clients.get(ws);
  if (!client) return;
  const peers = rooms.get(client.room);
  if (!peers) return;
  peers.delete(ws);
  if (peers.size === 0) rooms.delete(client.room);
}

function roomSize(room) {
  return rooms.get(room)?.size ?? 0;
}

function send(ws, payload) {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(payload));
  }
}

function broadcast(room, payload) {
  const peers = rooms.get(room);
  if (!peers) return;
  const body = JSON.stringify(payload);
  for (const peer of peers) {
    if (peer.readyState === WebSocket.OPEN) {
      peer.send(body);
    }
  }
}

function remember(room, message) {
  const history = histories.get(room) ?? [];
  history.push(message);
  while (history.length > HISTORY_LIMIT) history.shift();
  histories.set(room, history);
}

function consumeQuota(client) {
  const now = Date.now();
  if (now > client.rateResetAt) {
    client.rateResetAt = now + RATE_WINDOW_MS;
    client.messagesInWindow = 0;
  }
  client.messagesInWindow += 1;
  return client.messagesInWindow <= RATE_LIMIT;
}

index.html

<!doctype html>
<html lang="hi">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>WebSocket चैट डेमो</title>
    <style>
      body { font-family: system-ui, sans-serif; max-width: 760px; margin: 32px auto; padding: 0 16px; }
      label { display: block; margin-top: 12px; }
      input, button { font: inherit; padding: 8px; }
      #log { border: 1px solid #ccc; min-height: 240px; padding: 12px; overflow: auto; }
      .message { margin: 0 0 8px; }
      .muted { color: #666; }
      .error { color: #b00020; }
    </style>
  </head>
  <body>
    <h1>WebSocket चैट डेमो</h1>
    <p id="status" class="muted">offline</p>

    <label>कमरा <input id="room" value="lobby" /></label>
    <label>नाम <input id="name" value="masa" /></label>
    <label>टोकन <input id="token" value="dev-token" /></label>
    <button id="connect">जोड़ें</button>
    <button id="disconnect">तोड़ें</button>

    <div id="log" aria-live="polite"></div>

    <form id="form">
      <label>संदेश <input id="message" autocomplete="off" maxlength="500" /></label>
      <button type="submit">भेजें</button>
    </form>

    <script>
      const statusEl = document.querySelector("#status");
      const logEl = document.querySelector("#log");
      const formEl = document.querySelector("#form");
      const roomEl = document.querySelector("#room");
      const nameEl = document.querySelector("#name");
      const tokenEl = document.querySelector("#token");
      const messageEl = document.querySelector("#message");
      const connectEl = document.querySelector("#connect");
      const disconnectEl = document.querySelector("#disconnect");

      let socket;
      let reconnectTimer;
      let reconnectAttempt = 0;
      let manuallyClosed = false;
      const pendingMessages = [];

      connectEl.addEventListener("click", connect);
      disconnectEl.addEventListener("click", () => {
        manuallyClosed = true;
        clearTimeout(reconnectTimer);
        if (socket) socket.close(1000, "user disconnected");
      });

      formEl.addEventListener("submit", (event) => {
        event.preventDefault();
        const text = messageEl.value.trim();
        if (!text) return;
        const payload = JSON.stringify({ type: "message", text });

        if (socket?.readyState === WebSocket.OPEN && socket.bufferedAmount < 64 * 1024) {
          socket.send(payload);
        } else {
          pendingMessages.push(payload);
          writeLog("Socket तैयार नहीं है, message queue में रखा गया।", "muted");
        }

        messageEl.value = "";
      });

      function connect() {
        manuallyClosed = false;
        clearTimeout(reconnectTimer);
        if (socket && socket.readyState === WebSocket.OPEN) return;

        const params = new URLSearchParams({
          token: tokenEl.value,
          name: nameEl.value,
          room: roomEl.value
        });
        const protocol = location.protocol === "https:" ? "wss:" : "ws:";
        socket = new WebSocket(`${protocol}//${location.host}/chat?${params.toString()}`);
        setStatus("connecting");

        socket.addEventListener("open", () => {
          reconnectAttempt = 0;
          setStatus("online");
          flushPending();
        });

        socket.addEventListener("message", (event) => {
          const data = JSON.parse(event.data);
          renderEvent(data);
        });

        socket.addEventListener("close", (event) => {
          setStatus(`closed ${event.code}`);
          if (!manuallyClosed) scheduleReconnect();
        });

        socket.addEventListener("error", () => {
          writeLog("WebSocket error. Server log देखें।", "error");
        });
      }

      function scheduleReconnect() {
        const delay = Math.min(1000 * 2 ** reconnectAttempt, 10000) + Math.floor(Math.random() * 250);
        reconnectAttempt += 1;
        setStatus(`${delay}ms में reconnect`);
        reconnectTimer = setTimeout(connect, delay);
      }

      function flushPending() {
        while (pendingMessages.length > 0 && socket.readyState === WebSocket.OPEN && socket.bufferedAmount < 64 * 1024) {
          socket.send(pendingMessages.shift());
        }
      }

      function renderEvent(data) {
        if (data.type === "message") {
          writeLog(`${data.message.from}: ${data.message.text}`);
        } else if (data.type === "history") {
          writeLog(`${data.messages.length} पुराने messages loaded.`, "muted");
          data.messages.forEach((message) => writeLog(`${message.from}: ${message.text}`));
        } else if (data.type === "error") {
          writeLog(`${data.code}: ${data.message}`, "error");
        } else {
          writeLog(data.message ?? JSON.stringify(data), "muted");
        }
      }

      function setStatus(value) {
        statusEl.textContent = value;
      }

      function writeLog(value, className = "message") {
        const line = document.createElement("p");
        line.className = className;
        line.textContent = value;
        logEl.append(line);
        logEl.scrollTop = logEl.scrollHeight;
      }

      connect();
    </script>
  </body>
</html>

smoke-client.mjs

import { WebSocket } from "ws";

const url = new URL("ws://localhost:8080/chat");
url.searchParams.set("token", process.env.CHAT_TOKEN ?? "dev-token");
url.searchParams.set("name", "smoke");
url.searchParams.set("room", "lobby");

const ws = new WebSocket(url, {
  headers: {
    Origin: "http://localhost:8080"
  }
});

const timeout = setTimeout(() => {
  console.error("smoke test timed out");
  ws.terminate();
  process.exit(1);
}, 5000);

ws.on("open", () => {
  ws.send(JSON.stringify({ type: "message", text: "hello from smoke test" }));
});

ws.on("message", (raw) => {
  const data = JSON.parse(raw.toString("utf8"));
  console.log(data);
  if (data.type === "message" && data.message.text === "hello from smoke test") {
    clearTimeout(timeout);
    ws.close(1000, "done");
  }
});

ws.on("close", (code, reason) => {
  console.log(`closed ${code} ${reason.toString()}`);
  process.exit(code === 1000 ? 0 : 1);
});

ws.on("error", (error) => {
  clearTimeout(timeout);
  console.error(error);
  process.exit(1);
});
npm run smoke

सामान्य गलतियां

गलतीअसरसुधार
upgrade में auth नहींUntrusted socket active हो जाता हैhandleUpgrade से पहले reject
Origin check नहींCross-site WebSocket riskAllowed origins list
Rate limit नहींएक client CPU और memory खा सकता हैConnection, user, IP limit
Unlimited memory historyProcess धीरे-धीरे भारी होगाCount, age और storage policy
Immediate reconnect loopRecovery में traffic spikeExponential backoff और jitter
Single-process fan-outMulti-server setup में message missRedis Pub/Sub, NATS या queue

उत्पादन और आय मार्ग

Account, payment, support या personal data हो तो wss:// इस्तेमाल करें। Authorization सिर्फ connection पर नहीं, room join, send, delete और moderation पर भी check होनी चाहिए। History database में जाए; multiple Node processes के लिए shared Pub/Sub चाहिए।

Solo learning के लिए free Claude Code cheatsheet से शुरू करें। Reusable prompts और setup material चाहिए तो ClaudeCodeLab products देखें। Team को authentication, audit log, Pub/Sub, CI और review rules real repository में fit करने हों तो Claude Code training और consultation बेहतर अगला कदम है।

Masa ने इस workflow को चलाकर देखा: server.js start करने के बाद http://localhost:8080 दो tabs में खोला, और same lobby room की messages दोनों tabs में तुरंत दिखीं। npm run smoke ने hello from smoke test send और receive किया। Origin बदलने पर 403 और token बदलने पर 401 मिला। Reconnect, rate limit, Origin validation और 50-message history को prompt में पहले लिखने से Claude Code का output review करना आसान हुआ।

#Claude Code #WebSocket #चैट #रीयल टाइम #Node.js
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.