{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/azure-web-pubsub-ts",
  "version": "1.0.0",
  "name": "Azure Web Pubsub Ts",
  "description": "Build real-time messaging applications using Azure Web PubSub SDKs for JavaScript (@azure/web-pubsub, @azure/web-pubsub-client). Use when implementing WebSocket-based real-time features, pub/sub me...",
  "system_prompt_fragment": "# Azure Web PubSub SDKs for TypeScript\n\nReal-time messaging with WebSocket connections and pub/sub patterns.\n\n## Installation\n\n```bash\n# Server-side management\nnpm install @azure/web-pubsub @azure/identity\n\n# Client-side real-time messaging\nnpm install @azure/web-pubsub-client\n\n# Express middleware for event handlers\nnpm install @azure/web-pubsub-express\n```\n\n## Environment Variables\n\n```bash\nWEBPUBSUB_CONNECTION_STRING=Endpoint=https://<resource>.webpubsub.azure.com;AccessKey=<key>;Version=1.0;\nWEBPUBSUB_ENDPOINT=https://<resource>.webpubsub.azure.com\n```\n\n## Server-Side: WebPubSubServiceClient\n\n### Authentication\n\n```typescript\nimport { WebPubSubServiceClient, AzureKeyCredential } from \"@azure/web-pubsub\";\nimport { DefaultAzureCredential } from \"@azure/identity\";\n\n// Connection string\nconst client = new WebPubSubServiceClient(\n  process.env.WEBPUBSUB_CONNECTION_STRING!,\n  \"chat\"  // hub name\n);\n\n// DefaultAzureCredential (recommended)\nconst client2 = new WebPubSubServiceClient(\n  process.env.WEBPUBSUB_ENDPOINT!,\n  new DefaultAzureCredential(),\n  \"chat\"\n);\n\n// AzureKeyCredential\nconst client3 = new WebPubSubServiceClient(\n  process.env.WEBPUBSUB_ENDPOINT!,\n  new AzureKeyCredential(\"<access-key>\"),\n  \"chat\"\n);\n```\n\n### Generate Client Access Token\n\n```typescript\n// Basic token\nconst token = await client.getClientAccessToken();\nconsole.log(token.url);  // wss://...?access_token=...\n\n// Token with user ID\nconst userToken = await client.getClientAccessToken({\n  userId: \"user123\",\n});\n\n// Token with permissions\nconst permToken = await client.getClientAccessToken({\n  userId: \"user123\",\n  roles: [\n    \"webpubsub.joinLeaveGroup\",\n    \"webpubsub.sendToGroup\",\n    \"webpubsub.sendToGroup.chat-room\",  // specific group\n  ],\n  groups: [\"chat-room\"],  // auto-join on connect\n  expirationTimeInMinutes: 60,\n});\n```\n\n### Send Messages\n\n```typescript\n// Broadcast to all connections in hub\nawait client.sendToAll({ message: \"Hello everyone!\" });\nawait client.sendToAll(\"Plain text\", { contentType: \"text/plain\" });\n\n// Send to specific user (all their connections)\nawait client.sendToUser(\"user123\", { message: \"Hello!\" });\n\n// Send to specific connection\nawait client.sendToConnection(\"connectionId\", { data: \"Direct message\" });\n\n// Send with filter (OData syntax)\nawait client.sendToAll({ message: \"Filtered\" }, {\n  filter: \"userId ne 'admin'\",\n});\n```\n\n### Group Management\n\n```typescript\nconst group = client.group(\"chat-room\");\n\n// Add user/connection to group\nawait group.addUser(\"user123\");\nawait group.addConnection(\"connectionId\");\n\n// Remove from group\nawait group.removeUser(\"user123\");\n\n// Send to group\nawait group.sendToAll({ message: \"Group message\" });\n\n// Close all connections in group\nawait group.closeAllConnections({ reason: \"Maintenance\" });\n```\n\n### Connection Management\n\n```typescript\n// Check existence\nconst userExists = await client.userExists(\"user123\");\nconst connExists = await client.connectionExists(\"connectionId\");\n\n// Close connections\nawait client.closeConnection(\"connectionId\", { reason: \"Kicked\" });\nawait client.closeUserConnections(\"user123\");\nawait client.closeAllConnections();\n\n// Permissions\nawait client.grantPermission(\"connectionId\", \"sendToGroup\", { targetName: \"chat\" });\nawait client.revokePermission(\"connectionId\", \"sendToGroup\", { targetName: \"chat\" });\n```\n\n## Client-Side: WebPubSubClient\n\n### Connect\n\n```typescript\nimport { WebPubSubClient } from \"@azure/web-pubsub-client\";\n\n// Direct URL\nconst client = new WebPubSubClient(\"<client-access-url>\");\n\n// Dynamic URL from negotiate endpoint\nconst client2 = new WebPubSubClient({\n  getClientAccessUrl: async () => {\n    const response = await fetch(\"/negotiate\");\n    const { url } = await response.json();\n    return url;\n  },\n});\n\n// Register handlers BEFORE starting\nclient.on(\"connected\", (e) => {\n  console.log(`Connected: ${e.connectionId}`);\n});\n\nclient.on(\"group-message\", (e) => {\n  console.log(`${e.message.group}: ${e.message.data}`);\n});\n\nawait client.start();\n```\n\n### Send Messages\n\n```typescript\n// Join group first\nawait client.joinGroup(\"chat-room\");\n\n// Send to group\nawait client.sendToGroup(\"chat-room\", \"Hello!\", \"text\");\nawait client.sendToGroup(\"chat-room\", { type: \"message\", content: \"Hi\" }, \"json\");\n\n// Send options\nawait client.sendToGroup(\"chat-room\", \"Hello\", \"text\", {\n  noEcho: true,        // Don't echo back to sender\n  fireAndForget: true, // Don't wait for ack\n});\n\n// Send event to server\nawait client.sendEvent(\"userAction\", { action: \"typing\" }, \"json\");\n```\n\n### Event Handlers\n\n```typescript\n// Connection lifecycle\nclient.on(\"connected\", (e) => {\n  console.log(`Connected: ${e.connectionId}, User: ${e.userId}`);\n});\n\nclient.on(\"disconnected\", (e) => {\n  console.log(`Disconnected: ${e.message}`);\n});\n\nclient.on(\"stopped\", () => {\n  console.log(\"Client stopped\");\n});\n\n// Messages\nclient.on(\"group-message\", (e) => {\n  console.log(`[${e.message.group}] ${e.message.fromUserId}: ${e.message.data}`);\n});\n\nclient.on(\"server-message\", (e) => {\n  console.log(`Server: ${e.message.data}`);\n});\n\n// Rejoin failure\nclient.on(\"rejoin-group-failed\", (e) => {\n  console.log(`Failed to rejoin ${e.group}: ${e.error}`);\n});\n```\n\n## Express Event Handler\n\n```typescript\nimport express from \"express\";\nimport { WebPubSubEventHandler } from \"@azure/web-pubsub-express\";\n\nconst app = express();\n\nconst handler = new WebPubSubEventHandler(\"chat\", {\n  path: \"/api/webpubsub/hubs/chat/\",\n  \n  // Blocking: approve/reject connection\n  handleConnect: (req, res) => {\n    if (!req.claims?.sub) {\n      res.fail(401, \"Authentication required\");\n      return;\n    }\n    res.success({\n      userId: req.claims.sub[0],\n      groups: [\"general\"],\n      roles: [\"webpubsub.sendToGroup\"],\n    });\n  },\n  \n  // Blocking: handle custom events\n  handleUserEvent: (req, res) => {\n    console.log(`Event from ${req.context.userId}:`, req.data);\n    res.success(`Received: ${req.data}`, \"text\");\n  },\n  \n  // Non-blocking\n  onConnected: (req) => {\n    console.log(`Client connected: ${req.context.connectionId}`);\n  },\n  \n  onDisconnected: (req) => {\n    console.log(`Client disconnected: ${req.context.connectionId}`);\n  },\n});\n\napp.use(handler.getMiddleware());\n\n// Negotiate endpoint\napp.get(\"/negotiate\", async (req, res) => {\n  const token = await serviceClient.getClientAccessToken({\n    userId: req.user?.id,\n  });\n  res.json({ url: token.url });\n});\n\napp.listen(8080);\n```\n\n## Key Types\n\n```typescript\n// Server\nimport {\n  WebPubSubServiceClient,\n  WebPubSubGroup,\n  GenerateClientTokenOptions,\n  HubSendToAllOptions,\n} from \"@azure/web-pubsub\";\n\n// Client\nimport {\n  WebPubSubClient,\n  WebPubSubClientOptions,\n  OnConnectedArgs,\n  OnGroupDataMessageArgs,\n} from \"@azure/web-pubsub-client\";\n\n// Express\nimport {\n  WebPubSubEventHandler,\n  ConnectRequest,\n  UserEventRequest,\n  ConnectResponseHandler,\n} from \"@azure/web-pubsub-express\";\n```\n\n## Best Practices\n\n1. **Use Entra ID auth** - `DefaultAzureCredential` for production\n2. **Register handlers before start** - Don't miss initial events\n3. **Use groups for channels** - Organize messages by topic/room\n4. **Handle reconnection** - Client auto-reconnects by default\n5. **Validate in handleConnect** - Reject unauthorized connections early\n6. **Use noEcho** - Prevent message echo back to sender when needed\n\n## When to Use\nThis skill is applicable to execute the workflow or actions described in the overview.",
  "applicable_domains": [
    "frontend"
  ],
  "category": "frontend",
  "invocation": [
    "/azure-web-pubsub-ts"
  ],
  "authored_by": "claudeskills.in community",
  "source_url": "https://claudeskills.in/skill/azure-web-pubsub-ts",
  "provenance": {
    "source": "claudeskills.in",
    "source_url": "https://claudeskills.in/skill/azure-web-pubsub-ts",
    "license": "unknown",
    "imported_at": "2026-09-03",
    "notes": "Aggregated by claudeskills.in from community GitHub lists."
  },
  "tags": [
    "claudeskills",
    "frontend"
  ],
  "lifecycle": "draft"
}